From 492124da636964abf2c1321edfe1557090e775b1 Mon Sep 17 00:00:00 2001 From: MIHAIL Date: Thu, 7 Dec 2017 19:15:32 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA?= =?UTF-8?q?=D0=B0=20Opencart=203.=20=D0=A8=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=BF=D0=BE=D0=B4=20=D1=81=D0=B4=D0=B5=D0=BB=D0=B0?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BF=D0=BE=D0=B4=20twig.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extension/payment/shoputils_ik.php | 379 +++++++++++++++++ .../en-gb/extension/payment/shoputils_ik.php | 119 ++++++ .../ru-ru/extension/payment/shoputils_ik.php | 119 ++++++ .../admin/view/image/payment/shoputils_ik.gif | Bin 0 -> 3278 bytes .../view/image/payment/shoputils_ik23x30.gif | Bin 0 -> 1972 bytes .../extension/payment/shoputils_ik.twig | 386 ++++++++++++++++++ .../extension/payment/shoputils_ik.php | 211 ++++++++++ .../en-gb/extension/payment/shoputils_ik.php | 13 + .../ru-ru/extension/payment/shoputils_ik.php | 13 + .../model/extension/payment/shoputils_ik.php | 39 ++ .../view/theme/default/image/shoputils_ik.png | Bin 0 -> 30580 bytes .../extension/payment/shoputils_ik.twig | 38 ++ 12 files changed, 1317 insertions(+) create mode 100644 OpenCart_3/upload/admin/controller/extension/payment/shoputils_ik.php create mode 100644 OpenCart_3/upload/admin/language/en-gb/extension/payment/shoputils_ik.php create mode 100644 OpenCart_3/upload/admin/language/ru-ru/extension/payment/shoputils_ik.php create mode 100644 OpenCart_3/upload/admin/view/image/payment/shoputils_ik.gif create mode 100644 OpenCart_3/upload/admin/view/image/payment/shoputils_ik23x30.gif create mode 100644 OpenCart_3/upload/admin/view/template/extension/payment/shoputils_ik.twig create mode 100644 OpenCart_3/upload/catalog/controller/extension/payment/shoputils_ik.php create mode 100644 OpenCart_3/upload/catalog/language/en-gb/extension/payment/shoputils_ik.php create mode 100644 OpenCart_3/upload/catalog/language/ru-ru/extension/payment/shoputils_ik.php create mode 100644 OpenCart_3/upload/catalog/model/extension/payment/shoputils_ik.php create mode 100644 OpenCart_3/upload/catalog/view/theme/default/image/shoputils_ik.png create mode 100644 OpenCart_3/upload/catalog/view/theme/default/template/extension/payment/shoputils_ik.twig diff --git a/OpenCart_3/upload/admin/controller/extension/payment/shoputils_ik.php b/OpenCart_3/upload/admin/controller/extension/payment/shoputils_ik.php new file mode 100644 index 0000000..51dde22 --- /dev/null +++ b/OpenCart_3/upload/admin/controller/extension/payment/shoputils_ik.php @@ -0,0 +1,379 @@ +load->language('extension/payment/shoputils_ik'); + $this->document->setTitle($this->language->get('heading_title')); + } + + public function index() { + if (($this->request->server['REQUEST_METHOD'] == 'POST') && ($this->validate())) { + $this->_trimData(array( + 'payment_shoputils_ik_shop_id', + 'payment_shoputils_ik_sign_hash', + 'payment_shoputils_ik_sign_test_key', + 'payment_shoputils_ik_minimal_order', + 'payment_shoputils_ik_maximal_order' + )); + + $this->_replaceData(',', '.', array( + 'payment_shoputils_ik_minimal_order', + 'payment_shoputils_ik_maximal_order' + )); + + $this->load->model('setting/setting'); + + $this->model_setting_setting->editSetting('payment_shoputils_ik', $this->request->post); + $this->session->data['success'] = sprintf($this->language->get('text_success'), $this->language->get('heading_title')); + + $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=payment', true)); + } + + $this->load->model('localisation/currency'); + $this->load->model('localisation/geo_zone'); + $this->load->model('localisation/language'); + $this->load->model('localisation/order_status'); + + $ik_currencies = array( + '' => $this->language->get('text_currency_auto'), + 'RUB' => $this->language->get('text_currency_rub'), + 'UAH' => $this->language->get('text_currency_uah'), + 'USD' => $this->language->get('text_currency_usd'), + 'EUR' => $this->language->get('text_currency_eur') + ); + + $ik_lifetimes = array( + 5 => $this->language->get('text_lifetime_5minuts'), + 30 => $this->language->get('text_lifetime_30minuts'), + 60 => $this->language->get('text_lifetime_1hour'), + 1440 => $this->language->get('text_lifetime_1day'), + 10080 => $this->language->get('text_lifetime_1weekly'), + 43200 => $this->language->get('text_lifetime_30days') + ); + + $permission = $this->validatePermission(); + if (!$permission ) { + $this->error['warning'] = sprintf($this->language->get('error_permission'), $this->language->get('heading_title')); + } + + $server = isset($this->request->server['HTTPS']) && (($this->request->server['HTTPS'] == 'on') || ($this->request->server['HTTPS'] == '1')) ? HTTPS_CATALOG : HTTP_CATALOG; + + $data = $this->_setData(array( + 'heading_title', + 'button_save', + 'button_cancel', + 'button_clear', + 'tab_general', + 'tab_emails', + 'tab_settings', + 'tab_log', + 'tab_information', + 'lang', + 'text_confirm', + 'text_enabled', + 'text_disabled', + 'text_all_zones', + 'text_yes', + 'text_no', + 'text_info', + 'text_info_content', + 'text_parameters', + 'entry_geo_zone', + 'entry_status', + 'entry_sort_order', + 'entry_minimal_order', + 'entry_maximal_order', + 'entry_order_status', + 'entry_order_confirm_status', + 'entry_order_fail_status', + 'entry_laterpay_mode', + 'entry_order_later_status', + 'entry_title', + 'entry_instruction', + + 'entry_notify_customer_success', + 'entry_mail_customer_success_subject', + 'entry_mail_customer_success_content', + 'entry_notify_customer_fail', + 'entry_mail_customer_fail_subject', + 'entry_mail_customer_fail_content', + 'entry_notify_admin_success', + 'entry_mail_admin_success_subject', + 'entry_mail_admin_success_content', + 'entry_notify_admin_fail', + 'entry_mail_admin_fail_subject', + 'entry_mail_admin_fail_content', + + 'entry_shop_id', + 'entry_sign_hash', + 'entry_sign_test_key', + 'entry_test_mode', + 'entry_currency', + 'entry_lifetime', + 'entry_success_url', + 'entry_fail_url', + 'entry_pending_url', + 'entry_status_url', + + 'entry_log', + 'entry_log_file', + + 'placeholder_instruction', + + 'help_minimal_order', + 'help_maximal_order', + 'help_order_confirm_status', + 'help_order_status', + 'help_order_fail_status', + 'help_laterpay_mode', + 'help_order_later_status', + 'help_title', + 'help_instruction', + + 'help_notify_customer_success', + 'help_mail_customer_success_subject', + 'help_mail_customer_success_content', + 'help_notify_customer_fail', + 'help_mail_customer_fail_subject', + 'help_mail_customer_fail_content', + 'help_notify_admin_success', + 'help_mail_admin_success_subject', + 'help_mail_admin_success_content', + 'help_notify_admin_fail', + 'help_mail_admin_fail_subject', + 'help_mail_admin_fail_content', + + 'help_shop_id', + 'help_sign_hash', + 'help_sign_test_key', + 'help_test_mode', + 'help_currency', + 'help_lifetime', + 'help_log_file' => sprintf($this->language->get('help_log_file'), self::MAX_LAST_LOG_LINES), + 'help_log' => sprintf($this->language->get('help_log'), self::FILE_NAME_LOG), + 'title_default' => explode(',', $this->language->get('heading_title')), + 'action' => $this->makeUrl('extension/payment/shoputils_ik'), + 'cancel' => $this->makeUrl('extension/extension', 'type=payment'), + 'clear_log' => $this->makeUrl('extension/payment/shoputils_ik/clearLog'), + 'text_copyright' => sprintf($this->language->get('text_copyright'), $this->language->get('heading_title')), + 'payment_shoputils_ik_success_url' => $server . 'index.php?route=extension/payment/shoputils_ik/success', + 'payment_shoputils_ik_fail_url' => $server . 'index.php?route=extension/payment/shoputils_ik/fail', + 'payment_shoputils_ik_pending_url' => $server . 'index.php?route=extension/payment/shoputils_ik/success', + 'payment_shoputils_ik_status_url' => $server . 'index.php?route=extension/payment/shoputils_ik/status', + 'permission' => $permission, + 'error_warning' => isset($this->error['warning']) ? $this->error['warning'] : '', + 'error_shop_id' => isset($this->error['error_shop_id']) ? $this->error['error_shop_id'] : '', + 'error_sign_hash' => isset($this->error['error_sign_hash']) ? $this->error['error_sign_hash'] : '', + 'error_sign_test_key' => isset($this->error['error_sign_test_key']) ? $this->error['error_sign_test_key'] : '', + 'version' => $this->version, + 'log_lines' => $this->readLastLines(DIR_LOGS . 'payment_shoputils_ik.log', self::MAX_LAST_LOG_LINES), + 'log_filename' => self::FILE_NAME_LOG, + 'currencies' => array_intersect_key($ik_currencies, $this->model_localisation_currency->getCurrencies()), + 'lifetimes' => $ik_lifetimes, + 'geo_zones' => $this->model_localisation_geo_zone->getGeoZones(), + 'order_statuses' => array_merge(array(0 => array('order_status_id' => '0', 'name' => $this->language->get('text_order_status_cart'))), + $this->model_localisation_order_status->getOrderStatuses()), + 'oc_languages' => $this->model_localisation_language->getLanguages() + )); + + $data['breadcrumbs'][] = array( + 'href' => $this->makeUrl('common/dashboard'), + 'text' => $this->language->get('text_home') + ); + + $data['breadcrumbs'][] = array( + 'href' => $this->makeUrl('extension/extension', 'type=payment'), + 'text' => $this->language->get('text_extension') + ); + + $data['breadcrumbs'][] = array( + 'href' => $this->makeUrl('extension/payment/shoputils_ik'), + 'text' => $this->language->get('heading_title') + ); + + $data['logs'] = array( + '0' => $this->language->get('text_log_off'), + '1' => $this->language->get('text_log_short'), + '2' => $this->language->get('text_log_full') + ); + + $data['test_modes'] = array( + '0' => $this->language->get('text_disabled'), + '1' => $this->language->get('text_enabled'), + ); + + $data = array_merge($data, $this->_updateData( + array( + 'payment_shoputils_ik_geo_zone_id', + 'payment_shoputils_ik_sort_order', + 'payment_shoputils_ik_status', + 'payment_shoputils_ik_minimal_order', + 'payment_shoputils_ik_maximal_order', + 'payment_shoputils_ik_order_status_id', + 'payment_shoputils_ik_order_fail_status_id', + 'payment_shoputils_ik_order_confirm_status_id', + 'payment_shoputils_ik_langdata', + + 'payment_shoputils_ik_shop_id', + 'payment_shoputils_ik_sign_hash', + 'payment_shoputils_ik_sign_test_key', + 'payment_shoputils_ik_test_mode', + 'payment_shoputils_ik_currency', + 'payment_shoputils_ik_lifetime', + 'payment_shoputils_ik_log' + ), + array() + )); + + $data = array_merge($data, $this->_setData( + array( + 'header' => $this->load->controller('common/header'), + 'column_left' => $this->load->controller('common/column_left'), + 'footer' => $this->load->controller('common/footer') + ) + )); + + $this->response->setOutput($this->load->view('extension/payment/shoputils_ik', $data)); + } + + public function clearLog() { + $json = array(); + + if ($this->validatePermission()) { + if (is_file(DIR_LOGS . self::FILE_NAME_LOG)) { + @unlink(DIR_LOGS . self::FILE_NAME_LOG); + } + $json['success'] = $this->language->get('text_clear_log_success'); + } else { + $json['error'] = $this->language->get('error_clear_log'); + } + + $this->response->addHeader('Content-Type: application/json'); + $this->response->setOutput(json_encode($json)); + } + + protected function validate() { + if (!$this->validatePermission()) { + $this->error['warning'] = sprintf($this->language->get('error_permission'), $this->language->get('heading_title')); + } else { + if (!isset($this->request->post['payment_shoputils_ik_shop_id']) || !trim($this->request->post['payment_shoputils_ik_shop_id'])) { + $this->error['warning'] = $this->error['error_shop_id'] = sprintf($this->language->get('error_form'), + $this->language->get('entry_shop_id'), + $this->language->get('tab_settings')); + } + + if (!isset($this->request->post['payment_shoputils_ik_sign_hash']) || !trim($this->request->post['payment_shoputils_ik_sign_hash'])) { + $this->error['warning'] = $this->error['error_sign_hash'] = sprintf($this->language->get('error_form'), + $this->language->get('entry_sign_hash'), + $this->language->get('tab_settings')); + } + + if (!isset($this->request->post['payment_shoputils_ik_sign_test_key']) || !trim($this->request->post['payment_shoputils_ik_sign_test_key'])) { + $this->error['warning'] = $this->error['error_sign_test_key'] = sprintf($this->language->get('error_form'), + $this->language->get('entry_sign_test_key'), + $this->language->get('tab_settings')); + } + } + + return !$this->error; + } + + protected function _setData($values) { + $data = array(); + foreach ($values as $key => $value) { + if (is_int($key)) { + $data[$value] = $this->language->get($value); + } else { + $data[$key] = $value; + } + } + return $data; + } + + protected function _updateData($keys, $info = array()) { + $data = array(); + foreach ($keys as $key) { + if (isset($this->request->post[$key])) { + $data[$key] = $this->request->post[$key]; + } elseif (isset($info[$key])) { + $data[$key] = $info[$key]; + } else { + $data[$key] = $this->config->get($key); + } + } + return $data; + } + + protected function validatePermission() { + return $this->user->hasPermission('modify', 'extension/payment/shoputils_ik'); + } + + protected function _trimData($values) { + foreach ($values as $value) { + if (isset($this->request->post[$value])) { + $this->request->post[$value] = trim($this->request->post[$value]); + } + } + } + + protected function _replaceData($search, $replace, $values) { + foreach ($values as $value) { + if (isset($this->request->post[$value])) { + $this->request->post[$value] = str_replace($search, $replace, $this->request->post[$value]); + } + } + } + + protected function makeUrl($route, $url = '') { + return str_replace('&', '&', $this->url->link($route, $url . '&user_token=' . $this->session->data['user_token'], true)); + } + + protected function readLastLines($filename, $lines) { + if (!is_file($filename)) { + return array(); + } + $handle = @fopen($filename, "r"); + if (!$handle) { + return array(); + } + $linecounter = $lines; + $pos = -1; + $beginning = false; + $text = array(); + + while ($linecounter > 0) { + $t = " "; + + while ($t != "\n") { + /* if fseek() returns -1 we need to break the cycle*/ + if (fseek($handle, $pos, SEEK_END) == -1) { + $beginning = true; + break; + } + $t = fgetc($handle); + $pos--; + } + + $linecounter--; + + if ($beginning) { + rewind($handle); + } + + $text[$lines - $linecounter - 1] = fgets($handle); + + if ($beginning) { + break; + } + } + fclose($handle); + + return array_reverse($text); + } +} +?> \ No newline at end of file diff --git a/OpenCart_3/upload/admin/language/en-gb/extension/payment/shoputils_ik.php b/OpenCart_3/upload/admin/language/en-gb/extension/payment/shoputils_ik.php new file mode 100644 index 0000000..505fd72 --- /dev/null +++ b/OpenCart_3/upload/admin/language/en-gb/extension/payment/shoputils_ik.php @@ -0,0 +1,119 @@ +new.interkassa.com'; +$_['text_order_status_cart'] = 'Корзина (Ошибочный заказ)'; +$_['text_log_off'] = 'Выключен'; +$_['text_log_short'] = 'Частичный (Только результаты операций)'; +$_['text_log_full'] = 'Полный (Все запросы)'; + +$_['text_currency_auto'] = 'Определять автоматически'; +$_['text_currency_rub'] = 'Российские рубли (RUB-643)'; +$_['text_currency_uah'] = 'Украинские гривны (UAH-980)'; +$_['text_currency_usd'] = 'Доллары США (USD-840)'; +$_['text_currency_eur'] = 'Евро (EUR-978)'; +$_['text_lifetime_5minuts'] = '5 минут'; +$_['text_lifetime_30minuts'] = '30 минут'; +$_['text_lifetime_1hour'] = '1 час'; +$_['text_lifetime_1day'] = '1 день'; +$_['text_lifetime_1weekly'] = '1 неделя'; +$_['text_lifetime_30days'] = '30 дней'; +$_['text_copyright'] = 'Модуль оплаты "%s" разработан по заказу платежной системы "Interkassa" при поддержке ShopUtils. Если Вам требуется больше возможностей, возможно Вам потребуется более функциональный модуль.'; +$_['text_copyright'] = '© Разработано ShopUtils по заказу платежной системы "Interkassa". Расширенный функционал - в коммерческой версии модуля.'; +$_['text_info'] = 'После регистрации учетной записи интернет-магазина в «INTERKASSA» +необходимо настроить магазин для приема платежей. Сделать это можно в личном кабинете раздела Мои Кассы, нажав на кнопку "Настройки" +напротив своего магазина. Данные которые необходимо ввести в настройках магазина:'; +$_['text_info_content'] = 'В разделе "Мои кассы -> Настройки кассы -> Настройки платежей" необходимо установить следующие значения:
+"Проверять уникальность платежей" - вкл.
+"Передавать описание на платежную систему" - вкл.
+"Время жизни платежа (в минутах)" - вкл. "разрешить переопределять в запросе "


+В разделе "Мои кассы -> Настройки кассы -> Интерфейс" необходимо везде установить следующие значения:
+"Тип запроса" - "POST"
+везде выключить "Разрешить переопределять в запросе "


+В разделе "Мои кассы -> Настройки кассы -> Безопасность" необходимо установить следующие значения:
+"Алгоритм подписи" - "MD5"
+"Проверять подпись в форме запроса платежа" - вкл.
'; + +// Text FT +$_['text_order_id_ft'] = '№ заказа'; +$_['text_store_name_ft'] = 'Название магазина'; +$_['text_logo_ft'] = 'Логотип магазина'; +$_['text_products_ft'] = 'Список купленных товаров'; +$_['text_total_ft'] = 'Итого'; +$_['text_customer_firstname_ft'] = 'Имя Отчество покупателя'; +$_['text_customer_lastname_ft'] = 'Фамилия покупателя'; +$_['text_customer_group_ft'] = 'Группа покупателя'; +$_['text_customer_email_ft'] = 'e-mail покупателя'; +$_['text_customer_telephone_ft'] = 'Телефон покупателя'; +$_['text_order_status_ft'] = 'Статус заказа'; +$_['text_comment_ft'] = 'Комментарий покупателя к заказу'; +$_['text_ip_ft'] = 'IP адрес покупателя'; +$_['text_date_added_ft'] = 'Дата и время добавления заказа'; +$_['text_date_modified_ft'] = 'Дата и время изменения заказа'; + +// Entry +$_['entry_status'] = 'Статус'; +$_['entry_order_status'] = 'Статус заказа после оплаты'; +$_['entry_geo_zone'] = 'Географическая зона'; +$_['entry_sort_order'] = 'Порядок сортировки'; +$_['entry_minimal_order'] = 'Минимальная стоимость заказа'; +$_['entry_maximal_order'] = 'Максимальная стоимость заказа'; +$_['entry_order_confirm_status'] = 'Статус заказа после подтверждения'; +$_['entry_order_fail_status'] = 'Статус заказа после неудачной оплаты'; +$_['entry_title'] = 'Название'; +$_['entry_instruction'] = 'Инструкция по оплате'; + +$_['entry_shop_id'] = 'Идентификатор кассы'; +$_['entry_sign_hash'] = 'Секретный ключ'; +$_['entry_sign_test_key'] = 'Тестовый ключ'; +$_['entry_test_mode'] = 'Тестовый режим'; +$_['entry_currency'] = 'Валюта магазина'; +$_['entry_lifetime'] = 'Время жизни платежа'; + +$_['entry_log'] = 'Журнал'; +$_['entry_log_file'] = 'Файл журнала'; + +$_['entry_success_url'] = 'URL успешной оплаты'; +$_['entry_fail_url'] = 'URL неуспешной оплаты'; +$_['entry_pending_url'] = 'URL ожидания проведения платежа'; +$_['entry_status_url'] = 'URL взаимодействия'; + +// Placeholder +$_['placeholder_instruction'] = 'Уважаемый покупатель! Просим Вас дождаться звонка от нашего менеджера перед оплатой. Это необходимо для подтверждения наличия товара на складе и возможности доставки в Ваш регион. После чего менеджер отправит Вам письмо со ссылкой на оплату заказа.'; + +// Help +$_['help_order_confirm_status'] = 'При нажатии на кнопку "Подтвердить" на последнем этапе оформления заказа, заказу будет установлен выбранный статус'; +$_['help_order_status'] = 'После успешной оплаты заказа, заказу будет установлен выбранный статус.'; +$_['help_minimal_order'] = 'Если сумма заказа меньше указанной суммы, и сумма не пустая и не равна нулю, то этот метод оплаты не будет доступен, при оформлении заказа.
Например: 190.90'; +$_['help_maximal_order'] = 'Если сумма заказа больше указанной суммы, и сумма не пустая и не равна нулю, то этот метод оплаты не будет доступен, при оформлении заказа.
Например: 5000.01'; +$_['help_order_fail_status'] = 'Если Интеркасса вернет покупателя после неудачного платежа, заказу будет установлен выбранный статус.'; +$_['help_laterpay_mode'] = 'При включенном режиме отсроченной (отложенной) оплаты покупатель сможет оплатить заказ только после проверки заказа менеджером магазина.
Если Вам необходимо, чтобы у покупателя была возможность произвести оплату сразу после оформления заказа без подтверждения менеджером - не включайте эту опцию.'; +$_['help_order_later_status'] = 'После проверки заказа менеджер магазина выставит данный статус, покупатель будет уведомлен по e-mail и сможет оплатить заказ. Также, ссылка на оплату появится в личном кабинете покупателя в разделе "Мои заказы".
БУДЬТЕ ВНИМАТЕЛЬНЫ!
Если данный статус будет совпадать со "статус заказа после подтверждения" - режим отсроченной оплаты будет отключен и покупатели будут перенаправляться на сайт "Робокассы" для оплаты сразу после нажатия на кнопку "Оформить заказ".'; +$_['help_title'] = 'Название метода оплаты на странице оформления заказа'; +$_['help_instruction'] = 'Инструкция по оплате выводится при подтверждении заказа. Если поле не заполнено - инструкция по оплате выводиться не будет.'; + +$_['help_shop_id'] = 'Идентификатор кассы зарегистрированного в системе «INTERKASSA». Узнать его можно в разделе «Мои кассы». Пример: 529a6e08bf4efcae2d1b8488'; +$_['help_sign_hash'] = 'Используется SCI (Интеркассой) при формировании цифровой подписи. Должен совпадать с секретным ключем в разделе "Мои кассы -> Настройки кассы -> Безопасность -> Секретный ключ".'; +$_['help_sign_test_key'] = 'Используется SCI (Интеркассой) при формировании цифровой подписи, если платеж был совершен через тестовую платежную систему. Должен совпадать с тестовым ключем в разделе "Мои кассы -> Настройки кассы -> Безопасность -> Тестовый ключ".'; +$_['help_test_mode'] = 'В тестовом режиме можно проверить настройки модуля и Интеркассы через тестовую валюту Интеркассы, выбрав при оплате "Тестовая платежная система". В этом случае для подписывания ЭЦП (электронной цифровой подписи) используется тестовый ключ. Для приема реальных платежей - тестовый режим необходимо выключить.'; +$_['help_currency'] = 'Валюта, в которой магазин передает сумму платежа на платежный шлюз "Интеркасса". Интеркасса принимает следующие валюты: RUB, UAH, EUR, USD.'; +$_['help_lifetime'] = 'Платеж невозможно будет совершить по истечении срока жизни платежа после его создания.'; +$_['help_log_file'] = 'Последние %d строк из файла журнала.'; +$_['help_log'] = 'Журнал запросов от Интеркассы сохраняется в файле: /system/storage/logs/%s'; + +// Error +$_['error_permission'] = 'У Вас нет прав для управления модулем "%s"!'; +$_['error_clear_log'] = 'У Вас нет прав для очистки журнала модуля!'; +$_['error_form'] = 'Необходимо заполнить поле "%s" (вкладка "%s")!'; +?> \ No newline at end of file diff --git a/OpenCart_3/upload/admin/language/ru-ru/extension/payment/shoputils_ik.php b/OpenCart_3/upload/admin/language/ru-ru/extension/payment/shoputils_ik.php new file mode 100644 index 0000000..505fd72 --- /dev/null +++ b/OpenCart_3/upload/admin/language/ru-ru/extension/payment/shoputils_ik.php @@ -0,0 +1,119 @@ +new.interkassa.com'; +$_['text_order_status_cart'] = 'Корзина (Ошибочный заказ)'; +$_['text_log_off'] = 'Выключен'; +$_['text_log_short'] = 'Частичный (Только результаты операций)'; +$_['text_log_full'] = 'Полный (Все запросы)'; + +$_['text_currency_auto'] = 'Определять автоматически'; +$_['text_currency_rub'] = 'Российские рубли (RUB-643)'; +$_['text_currency_uah'] = 'Украинские гривны (UAH-980)'; +$_['text_currency_usd'] = 'Доллары США (USD-840)'; +$_['text_currency_eur'] = 'Евро (EUR-978)'; +$_['text_lifetime_5minuts'] = '5 минут'; +$_['text_lifetime_30minuts'] = '30 минут'; +$_['text_lifetime_1hour'] = '1 час'; +$_['text_lifetime_1day'] = '1 день'; +$_['text_lifetime_1weekly'] = '1 неделя'; +$_['text_lifetime_30days'] = '30 дней'; +$_['text_copyright'] = 'Модуль оплаты "%s" разработан по заказу платежной системы "Interkassa" при поддержке ShopUtils. Если Вам требуется больше возможностей, возможно Вам потребуется более функциональный модуль.'; +$_['text_copyright'] = '© Разработано ShopUtils по заказу платежной системы "Interkassa". Расширенный функционал - в коммерческой версии модуля.'; +$_['text_info'] = 'После регистрации учетной записи интернет-магазина в «INTERKASSA» +необходимо настроить магазин для приема платежей. Сделать это можно в личном кабинете раздела Мои Кассы, нажав на кнопку "Настройки" +напротив своего магазина. Данные которые необходимо ввести в настройках магазина:'; +$_['text_info_content'] = 'В разделе "Мои кассы -> Настройки кассы -> Настройки платежей" необходимо установить следующие значения:
+"Проверять уникальность платежей" - вкл.
+"Передавать описание на платежную систему" - вкл.
+"Время жизни платежа (в минутах)" - вкл. "разрешить переопределять в запросе "


+В разделе "Мои кассы -> Настройки кассы -> Интерфейс" необходимо везде установить следующие значения:
+"Тип запроса" - "POST"
+везде выключить "Разрешить переопределять в запросе "


+В разделе "Мои кассы -> Настройки кассы -> Безопасность" необходимо установить следующие значения:
+"Алгоритм подписи" - "MD5"
+"Проверять подпись в форме запроса платежа" - вкл.
'; + +// Text FT +$_['text_order_id_ft'] = '№ заказа'; +$_['text_store_name_ft'] = 'Название магазина'; +$_['text_logo_ft'] = 'Логотип магазина'; +$_['text_products_ft'] = 'Список купленных товаров'; +$_['text_total_ft'] = 'Итого'; +$_['text_customer_firstname_ft'] = 'Имя Отчество покупателя'; +$_['text_customer_lastname_ft'] = 'Фамилия покупателя'; +$_['text_customer_group_ft'] = 'Группа покупателя'; +$_['text_customer_email_ft'] = 'e-mail покупателя'; +$_['text_customer_telephone_ft'] = 'Телефон покупателя'; +$_['text_order_status_ft'] = 'Статус заказа'; +$_['text_comment_ft'] = 'Комментарий покупателя к заказу'; +$_['text_ip_ft'] = 'IP адрес покупателя'; +$_['text_date_added_ft'] = 'Дата и время добавления заказа'; +$_['text_date_modified_ft'] = 'Дата и время изменения заказа'; + +// Entry +$_['entry_status'] = 'Статус'; +$_['entry_order_status'] = 'Статус заказа после оплаты'; +$_['entry_geo_zone'] = 'Географическая зона'; +$_['entry_sort_order'] = 'Порядок сортировки'; +$_['entry_minimal_order'] = 'Минимальная стоимость заказа'; +$_['entry_maximal_order'] = 'Максимальная стоимость заказа'; +$_['entry_order_confirm_status'] = 'Статус заказа после подтверждения'; +$_['entry_order_fail_status'] = 'Статус заказа после неудачной оплаты'; +$_['entry_title'] = 'Название'; +$_['entry_instruction'] = 'Инструкция по оплате'; + +$_['entry_shop_id'] = 'Идентификатор кассы'; +$_['entry_sign_hash'] = 'Секретный ключ'; +$_['entry_sign_test_key'] = 'Тестовый ключ'; +$_['entry_test_mode'] = 'Тестовый режим'; +$_['entry_currency'] = 'Валюта магазина'; +$_['entry_lifetime'] = 'Время жизни платежа'; + +$_['entry_log'] = 'Журнал'; +$_['entry_log_file'] = 'Файл журнала'; + +$_['entry_success_url'] = 'URL успешной оплаты'; +$_['entry_fail_url'] = 'URL неуспешной оплаты'; +$_['entry_pending_url'] = 'URL ожидания проведения платежа'; +$_['entry_status_url'] = 'URL взаимодействия'; + +// Placeholder +$_['placeholder_instruction'] = 'Уважаемый покупатель! Просим Вас дождаться звонка от нашего менеджера перед оплатой. Это необходимо для подтверждения наличия товара на складе и возможности доставки в Ваш регион. После чего менеджер отправит Вам письмо со ссылкой на оплату заказа.'; + +// Help +$_['help_order_confirm_status'] = 'При нажатии на кнопку "Подтвердить" на последнем этапе оформления заказа, заказу будет установлен выбранный статус'; +$_['help_order_status'] = 'После успешной оплаты заказа, заказу будет установлен выбранный статус.'; +$_['help_minimal_order'] = 'Если сумма заказа меньше указанной суммы, и сумма не пустая и не равна нулю, то этот метод оплаты не будет доступен, при оформлении заказа.
Например: 190.90'; +$_['help_maximal_order'] = 'Если сумма заказа больше указанной суммы, и сумма не пустая и не равна нулю, то этот метод оплаты не будет доступен, при оформлении заказа.
Например: 5000.01'; +$_['help_order_fail_status'] = 'Если Интеркасса вернет покупателя после неудачного платежа, заказу будет установлен выбранный статус.'; +$_['help_laterpay_mode'] = 'При включенном режиме отсроченной (отложенной) оплаты покупатель сможет оплатить заказ только после проверки заказа менеджером магазина.
Если Вам необходимо, чтобы у покупателя была возможность произвести оплату сразу после оформления заказа без подтверждения менеджером - не включайте эту опцию.'; +$_['help_order_later_status'] = 'После проверки заказа менеджер магазина выставит данный статус, покупатель будет уведомлен по e-mail и сможет оплатить заказ. Также, ссылка на оплату появится в личном кабинете покупателя в разделе "Мои заказы".
БУДЬТЕ ВНИМАТЕЛЬНЫ!
Если данный статус будет совпадать со "статус заказа после подтверждения" - режим отсроченной оплаты будет отключен и покупатели будут перенаправляться на сайт "Робокассы" для оплаты сразу после нажатия на кнопку "Оформить заказ".'; +$_['help_title'] = 'Название метода оплаты на странице оформления заказа'; +$_['help_instruction'] = 'Инструкция по оплате выводится при подтверждении заказа. Если поле не заполнено - инструкция по оплате выводиться не будет.'; + +$_['help_shop_id'] = 'Идентификатор кассы зарегистрированного в системе «INTERKASSA». Узнать его можно в разделе «Мои кассы». Пример: 529a6e08bf4efcae2d1b8488'; +$_['help_sign_hash'] = 'Используется SCI (Интеркассой) при формировании цифровой подписи. Должен совпадать с секретным ключем в разделе "Мои кассы -> Настройки кассы -> Безопасность -> Секретный ключ".'; +$_['help_sign_test_key'] = 'Используется SCI (Интеркассой) при формировании цифровой подписи, если платеж был совершен через тестовую платежную систему. Должен совпадать с тестовым ключем в разделе "Мои кассы -> Настройки кассы -> Безопасность -> Тестовый ключ".'; +$_['help_test_mode'] = 'В тестовом режиме можно проверить настройки модуля и Интеркассы через тестовую валюту Интеркассы, выбрав при оплате "Тестовая платежная система". В этом случае для подписывания ЭЦП (электронной цифровой подписи) используется тестовый ключ. Для приема реальных платежей - тестовый режим необходимо выключить.'; +$_['help_currency'] = 'Валюта, в которой магазин передает сумму платежа на платежный шлюз "Интеркасса". Интеркасса принимает следующие валюты: RUB, UAH, EUR, USD.'; +$_['help_lifetime'] = 'Платеж невозможно будет совершить по истечении срока жизни платежа после его создания.'; +$_['help_log_file'] = 'Последние %d строк из файла журнала.'; +$_['help_log'] = 'Журнал запросов от Интеркассы сохраняется в файле: /system/storage/logs/%s'; + +// Error +$_['error_permission'] = 'У Вас нет прав для управления модулем "%s"!'; +$_['error_clear_log'] = 'У Вас нет прав для очистки журнала модуля!'; +$_['error_form'] = 'Необходимо заполнить поле "%s" (вкладка "%s")!'; +?> \ No newline at end of file diff --git a/OpenCart_3/upload/admin/view/image/payment/shoputils_ik.gif b/OpenCart_3/upload/admin/view/image/payment/shoputils_ik.gif new file mode 100644 index 0000000000000000000000000000000000000000..4380be049bbdc85bfc032bb335ff1280e13ca0bc GIT binary patch literal 3278 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 literal 0 HcmV?d00001 diff --git a/OpenCart_3/upload/admin/view/template/extension/payment/shoputils_ik.twig b/OpenCart_3/upload/admin/view/template/extension/payment/shoputils_ik.twig new file mode 100644 index 0000000..0d5cf70 --- /dev/null +++ b/OpenCart_3/upload/admin/view/template/extension/payment/shoputils_ik.twig @@ -0,0 +1,386 @@ +{{ header }}{{ column_left }} +
+ +
+ {% if error_warning %} +
{{ error_warning }} + +
+ {% endif %} +
+
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + {{ help_minimal_order }} +
+
+
+ +
+ + {{ help_maximal_order }} +
+
+
+ +
+ + {{ help_order_confirm_status }} +
+
+
+ +
+ + {{ help_order_status }} +
+
+
+ +
+ + {{ help_order_fail_status }} +
+
+
+ +
+ {% for language in oc_languages %} +
+ {% set language_image = language.code %} + + +
+ {% endfor %} + {{ help_title }} +
+
+
+ +
+ {% for language in oc_languages %} +
+ {% set language_image = language.code %} + + +
+ {% endfor %} + {{ help_instruction }} +
+
+
+ +
+
+ +
+ + {{ help_shop_id }} + {% if error_shop_id %} +
{{ error_shop_id }}
+ {% endif %} +
+
+ {% if permission %} +
+ +
+ + {{ help_sign_hash }} + {% if error_sign_hash %} +
{{ error_sign_hash }}
+ {% endif %} +
+
+
+ +
+ + {{ help_sign_test_key }} + {% if error_sign_test_key %} +
{{ error_sign_test_key }}
+ {% endif %} +
+
+ {% endif %} + +
+ +
+ + {{ help_test_mode }} +
+
+
+ +
+ + {{ help_currency }} +
+
+ + + +
+ +
+
+ +
+ + + + {{ help_log }} +
+ +
+ + +
+ +
+
+
{% for log_line, log_line in log_lines %}{% endfor %}
+
+ {{ help_log_file }} +
+
+
+ +
+
+ {{ text_info}} +
+
+ {{ text_info_content }} +
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+
+
+
+
+
{{ text_copyright }}
+
+
+
+
+ +{{ footer }} \ No newline at end of file diff --git a/OpenCart_3/upload/catalog/controller/extension/payment/shoputils_ik.php b/OpenCart_3/upload/catalog/controller/extension/payment/shoputils_ik.php new file mode 100644 index 0000000..8004698 --- /dev/null +++ b/OpenCart_3/upload/catalog/controller/extension/payment/shoputils_ik.php @@ -0,0 +1,211 @@ +load->language('extension/payment/shoputils_ik'); + } + + public function index() { + $langdata = $this->config->get('shoputils_ik_langdata'); + $this->_setData( + array( + 'button_confirm', + 'instruction' => nl2br($langdata[$this->config->get('config_language_id')]['instruction']), + 'action' => self::$ACTION, + 'parameters' => $this->makeForm() + ) + ); + + return $this->load->view('extension/payment/shoputils_ik', $this->data); + } + + public function status() { + $this->logWrite('StatusURL: ', self::$LOG_SHORT); + $this->logWrite(' POST:' . var_export($this->request->post, true), self::$LOG_FULL); + $this->logWrite(' GET:' . var_export($this->request->get, true), self::$LOG_FULL); + + if (!$this->validate(true)) { + return; + } + + if ($this->request->post['ik_inv_st']) { + $this->load->model('checkout/order'); + $this->model_checkout_order->addOrderHistory($this->order['order_id'], + $this->config->get('shoputils_ik_order_status_id'), + sprintf($this->language->get('text_comment'), $this->request->post['ik_pw_via'], $this->request->post['ik_am']), + true); + } + + $this->sendOk(); + } + + public function success() { + $this->logWrite('SuccessURL', self::$LOG_SHORT); + $this->logWrite(' POST:' . var_export($this->request->post, true), self::$LOG_FULL); + $this->logWrite(' GET:' . var_export($this->request->get, true), self::$LOG_FULL); + + if ($this->validate(false)) { + if (!isset($this->session->data['order_id'])) { + $this->session->data['order_id'] = $this->order['order_id']; //Добавляем в сессию номер заказа на случай, если в checkout/success на экран пользователю выводится номер заказа + } + $this->response->redirect($this->url->link('checkout/success', '', 'SSL')); + } else { + $this->response->redirect($this->url->link('checkout/failure', '', 'SSL')); + } + } + + public function fail() { + $this->logWrite('FailURL', self::$LOG_SHORT); + $this->logWrite(' POST:' . var_export($this->request->post, true), self::$LOG_FULL); + $this->logWrite(' GET:' . var_export($this->request->get, true), self::$LOG_FULL); + if ($this->validate(false)) { + $this->load->model('checkout/order'); + $this->model_checkout_order->addOrderHistory($this->order['order_id'], + $this->config->get('shoputils_ik_order_fail_status_id'), + $this->language->get('text_comment_fail'), + true); + } + + $this->response->redirect($this->url->link('checkout/failure', '', 'SSL')); + } + + public function confirm() { + if (!empty($this->session->data['order_id']) && $this->config->get('shoputils_ik_order_confirm_status_id') && ($this->session->data['payment_method']['code'] == 'shoputils_ik')) { + $this->load->model('checkout/order'); + $this->model_checkout_order->addOrderHistory($this->session->data['order_id'], $this->config->get('shoputils_ik_order_confirm_status_id')); + } + } + + protected function makeForm($order_id = false) { + $this->load->model('checkout/order'); + if (!$order_id ) { + if (isset($this->session->data['order_id'])) { + $order_id = $this->session->data['order_id']; + } else { + $this->logWrite('Error: Unsupported Checkout Extension', self::$LOG_SHORT); + die($this->language->get('error_fail_checkout_extension')); + } + } + $order_info = $this->model_checkout_order->getOrder($order_id); + + $ikCurrencyCode = $this->config->get('shoputils_ik_currency'); + if (!$this->currency->has($ikCurrencyCode)) { + die(sprintf('Currency code (for code: %s) not found', $ikCurrencyCode)); + } + + $ik_payment_amount = number_format($this->currency->convert($order_info['total'], $this->config->get('config_currency'), $ikCurrencyCode), 2, '.', ''); + $ik_shop_id = $this->config->get('shoputils_ik_shop_id'); + $ik_payment_desc = sprintf($this->language->get('text_ik_payment_desc'), $order_info['order_id']); + $ik_lifetime = $this->config->get('shoputils_ik_lifetime'); + + $params = array( + 'ik_am' => $ik_payment_amount, + 'ik_co_id' => $ik_shop_id, + 'ik_cur' => $ikCurrencyCode, + 'ik_desc' => $ik_payment_desc, + //'ik_ltm' => $ik_lifetime, + 'ik_pm_no' => $order_info['order_id'] + ); + $params['ik_sign'] = base64_encode(md5(implode(':', array_values($params)) . ':' . $this->config->get('shoputils_ik_sign_hash'), true)); + + $this->logWrite('Make payment form: ', self::$LOG_SHORT); + $this->logWrite(' DATA: ' . var_export($params, true), self::$LOG_FULL); + + return $params; + } + + protected function validate($check_sign_hash = true) { + $this->load->model('checkout/order'); + + if ((ip2long($this->request->server['REMOTE_ADDR']) < ip2long('151.80.190.97')) && (ip2long($this->request->server['REMOTE_ADDR']) > ip2long('151.80.190.104'))) { + $this->sendForbidden(sprintf($this->language->get('text_error_allowed_range_ip'), $this->request->server['REMOTE_ADDR'])); + return false; + } + + if ($this->request->server['REQUEST_METHOD'] != 'POST') { + $this->sendForbidden($this->language->get('text_error_post')); + return false; + } + + if ($check_sign_hash && isset($sign_ik)) { + $ik_sign_hash_array = $this->request->post; + unset($ik_sign_hash_array['ik_sign']); + ksort($ik_sign_hash_array, SORT_STRING); + array_push($ik_sign_hash_array, $this->config->get('shoputils_ik_test_mode') ? $this->config->get('shoputils_ik_sign_test_key') : $this->config->get('shoputils_ik_sign_hash')); //$this->config->get('shoputils_ik_sign_hash'); + $ik_sign_hash_string = implode(':', $ik_sign_hash_array); + $ik_sign_hash = base64_encode(md5($ik_sign_hash_string, true)); + + if ($this->request->post['ik_sign'] != $ik_sign_hash) { + $this->sendForbidden($this->language->get('text_error_ik_sign_hash')); + $this->logWrite($ik_sign_hash . '=md5(' . $ik_sign_hash_string . ')', self::$LOG_SHORT); + return false; + } + } + + $this->order = $this->model_checkout_order->getOrder($this->request->post['ik_pm_no']); + + if (!$this->order) { + $this->sendForbidden(sprintf($this->language->get('text_error_order_not_found'), $this->request->post['ik_pm_no'])); + return false; + } + + return true; + } + + protected function sendForbidden($error) { + $this->logWrite('ERROR: ' . $error, self::$LOG_SHORT); + $this->response->addHeader('HTTP/1.1 403 Forbidden'); + //header('HTTP/1.1 403 Forbidden'); + echo " + + 403 Forbidden + + +

$error.

+ + "; + } + + protected function sendOk() { + $this->logWrite('OK: ' . http_build_query($this->request->post, '', ','), self::$LOG_SHORT); + $this->response->addHeader('HTTP/1.1 200 OK'); + //header('HTTP/1.1 200 OK'); + echo "200 OK"; + } + + protected function _setData($values) { + $this->data = array(); + foreach ($values as $key => $value) { + if (is_int($key)) { + $this->data[$value] = $this->language->get($value); + } else { + $this->data[$key] = $value; + } + } + } + + protected function logWrite($message, $type) { + switch ($this->config->get('shoputils_ik_log')) { + case self::$LOG_OFF: + return; + case self::$LOG_SHORT: + if ($type == self::$LOG_FULL) { + return; + } + } + + if (!$this->log) { + $this->log = new Log($this->config->get('shoputils_ik_log_filename')); + } + $this->log->Write($message); + } +} +?> \ No newline at end of file diff --git a/OpenCart_3/upload/catalog/language/en-gb/extension/payment/shoputils_ik.php b/OpenCart_3/upload/catalog/language/en-gb/extension/payment/shoputils_ik.php new file mode 100644 index 0000000..ff74d1e --- /dev/null +++ b/OpenCart_3/upload/catalog/language/en-gb/extension/payment/shoputils_ik.php @@ -0,0 +1,13 @@ +Методы оплаты'; + +$_['text_error_allowed_range_ip'] = 'Удаленный IP шлюза %s не из разрешенного диапазона'; +$_['text_error_post'] = 'Ответ от шлюза не типа POST'; +$_['text_error_ik_sign_hash'] = 'Некорректная подпись сообщения от платёжного шлюза'; +$_['text_error_order_not_found'] = 'Заказ № "%s" не найден'; +?> \ No newline at end of file diff --git a/OpenCart_3/upload/catalog/language/ru-ru/extension/payment/shoputils_ik.php b/OpenCart_3/upload/catalog/language/ru-ru/extension/payment/shoputils_ik.php new file mode 100644 index 0000000..ff74d1e --- /dev/null +++ b/OpenCart_3/upload/catalog/language/ru-ru/extension/payment/shoputils_ik.php @@ -0,0 +1,13 @@ +Методы оплаты'; + +$_['text_error_allowed_range_ip'] = 'Удаленный IP шлюза %s не из разрешенного диапазона'; +$_['text_error_post'] = 'Ответ от шлюза не типа POST'; +$_['text_error_ik_sign_hash'] = 'Некорректная подпись сообщения от платёжного шлюза'; +$_['text_error_order_not_found'] = 'Заказ № "%s" не найден'; +?> \ No newline at end of file diff --git a/OpenCart_3/upload/catalog/model/extension/payment/shoputils_ik.php b/OpenCart_3/upload/catalog/model/extension/payment/shoputils_ik.php new file mode 100644 index 0000000..53ba2c2 --- /dev/null +++ b/OpenCart_3/upload/catalog/model/extension/payment/shoputils_ik.php @@ -0,0 +1,39 @@ +load->language('extension/payment/payment_shoputils_ik'); + + if (($this->config->get('payment_shoputils_ik_status')) && ($total) && + (!$this->config->get('payment_shoputils_ik_minimal_order') || ($total >= (float)$this->config->get('payment_shoputils_ik_minimal_order'))) && + (!$this->config->get('payment_shoputils_ik_maximal_order') || ($total <= (float)$this->config->get('payment_shoputils_ik_maximal_order')))) { + $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "zone_to_geo_zone WHERE geo_zone_id = '" . (int) $this->config->get('payment_shoputils_ik_geo_zone_id') . "' AND country_id = '" . (int) $address['country_id'] . "' AND (zone_id = '" . (int) $address['zone_id'] . "' OR zone_id = '0')"); + + if (!$this->config->get('payment_shoputils_ik_geo_zone_id')) { + $status = true; + } elseif ($query->num_rows) { + $status = true; + } else { + $status = false; + } + } else { + $status = false; + } + + $method_data = array(); + + if ($status) { + $title = $this->config->get('payment_shoputils_ik_langdata'); + $server = isset($this->request->server['HTTPS']) && (($this->request->server['HTTPS'] == 'on') || ($this->request->server['HTTPS'] == '1')) ? $this->config->get('config_ssl') : $this->config->get('config_url'); + + $method_data = array( + 'code' => 'shoputils_ik', + 'title' => $title[$this->config->get('config_language_id')]['title'], + 'description' => sprintf($this->language->get('text_description'), $server), + 'terms' => '', + 'sort_order' => $this->config->get('payment_shoputils_ik_sort_order') + ); + } + return $method_data; + } +} +?> \ No newline at end of file diff --git a/OpenCart_3/upload/catalog/view/theme/default/image/shoputils_ik.png b/OpenCart_3/upload/catalog/view/theme/default/image/shoputils_ik.png new file mode 100644 index 0000000000000000000000000000000000000000..39e32a829ad4e2c47af1ae9fac4c78a83d006ad6 GIT binary patch literal 30580 zcmV)HK)t_-P)PcR%r zqEZQqNsDn~Mj<9Ki5N05V*!w)1cV4^=|8z#Mr@g3^-n(`)@p@Hb7TPZtz8JSx@KRlpLJtqI_FzDQPOXAYsiGRG=yXVz8emXcNnudI6(-=$9enf$KAnnDZx`z= zz2)?mtQntG(}($F)aCVske+D3nm5kJ>_0BYH$T-9^N{dsMTwm1Fd0>-ntNH)FFAkp zmdXx~FMxhI2}p^zMDN?bWmjDXzqg@P2ouQp)gHva zj-`$9_^B})TWWkb&u?c6m8l2fI=NZh5F|#eL2_&y_EOK;)zF1Rg93VD%0%8_b^4K- zV8m$ZIvN69Mj+l9bR6-%99z~yuTx@gSdW;HivV7WkX{L=-A&A0LQ+OH)WqOhI=T>- z5QFp30;RZ$0=}>uaXEdD%D7Jt*%p_AV$q_i>bg3YS*_9aX!eK);n2T(p4{U*q+`59 z{!fVOPq^lq{HR{B-CKGK$t}QeatE{or^AOCfUp4-whKZg469`cm44T1nAzDymu=z8+1Z&ygg@q)VeLz9Jmj^7r87- zA|*ohGd{!*_H?k_?OyC`^I%Gb5e|Qlz*~V0%`S9$g9O|vc56}mNfEF0D^{|q_um7m zE1`D#5O%rX3x&agB)2^~BJLfd~9x3guz<)U|CTlhu|Yq&1+}VM3cJ3VOK&qs9O^DS1&w zBit@GJSvHlRvCONzBva6Xjp4w3+&Af5zn-}^Y=E!4oG?EKWqLaF zLxw;A<0l7tQL|s9!MGsBu|vkTYAfXUJ0IxN(25da*~ahFgWp7 z*VWSD{Z$sg^TzkXx65{5TS)^BV%w-~?u3#A@s83aj2)UI_HWx+kIT**bQBbjb<+&x zQLpu{tu5@M-8S4gBo+!f{MLcd*x2mEfOsALw%&?97Bybj)Cymav3rIl{3@BS>x1`M z^=q%g;&#E`-USx{H$9LDXd?M9z=Lm!rK?~s{|#hszQy9ljQg*XL!5Oh5rBcUV@DI< ziB_SjONS_f25NdqyWK!kK`c^t6;od$9jLU1fV*eFwkHr4AgZL}gMk1V+MHNf+zg`> zfZXqeN~^_QXAmi|2AtI|4q;LVLNa2|VcFqqd)*Jv1sv$qCBUOJAWo%*Nw0>7>QU(Q zQ0hn_2&>`KXz0!zhz(egD))d%YJ>tlcvI35mzr|K_gGa0+<6Ce0okc-Q)&X-cYnA+ zVX^3g3l=QHvZV`7`HQ?{R`zin=lJO-*7(F@a4uVc&=Ht`Ha`zZk3G&}uDnu$bsfW| zKLd4(X~cvCy_fbKDCfLp0K?!%fIe58(7vbhF@4} zot+)?N;bpo@gN{ET-t90qBVMaS+Wr!LXrUYX_3}>^}w;H>}bK7rV8Qn;%#r#=x|Zr zAy`ti6IN#z6d@9#YB|o#C_qz38%jGGsZJUIqzDd-Ps7>S5e>TD){ZasY{VI9eK9gM z2QI%4bLqQYp~j4Y;b?N&@YUYU7!aEZHL;2HO;tEAdjL!{;NRW00z;E>FfKiJqe3P- z=lBI1^%u_4Si~$XC+$BF6yCLnpdt;PN4av-BLC!5R(QWw$musak6XF;Ifq)p_SNC> zg`ic-54$d}Z}XluVO~#&)*db#sBRtv`bR;EMP*UCuYlof1N1LQ&YswHC zrAKRr6F+UN5Wrp4(8fCLUYt9z5LMxG+(MiTWzvP)pKKX=|{QVik2ZFHIHHv;D)9WBN=)ese9IOIfCwvY&LZoOo zO3EQU_5_R{f6TOriT^b+K}KUS#K#_qgvDM1R(s8ytSoa&-dDM|SJygFiiUo%j5wKcJ)R(^%c3rFF?ydg(??s=tK*}Geo zjWAxVf+Fy&bC%t1@6l;cx;{C7xLJCH{H# zRY*6-;IXB1Fge z`kahKrm3hk3UN|^j@8n6)D=m&3GR=u?O8MM;i57G3A}H+tWbR4xVsfDZxDIuW_&=u zT`(>KHUjVJMhAuw^NBHO(A4I}6R&Q<>wg&kt7TATN}N=bm&|@#vln|RT0p>!U1cqJ z?2fbX&@%!e;J#I_jeOOBpFZmd92%au;M45LX@owUTXX3OJ*Qu$Y413mZ5(BHv+hW0Y3~y z{n78dY4BQGQNCaiN|!B1@^_1ocipx7>)O73JNxj%4>5G;P+WG|WhYfml$DjSufP5p zT;aL)+G`OL6LZ?-5C=hyEg`576>`87v=RvBph-u-O8qrVpZb6T;h+pr3DHoS+6cJ) zguM3?u*NXFG9++lQKV4=f}^_c3n2U^@~LV^t_f(r^a=8f-q4 zx448>sZq-IvnUS<;62Sw5ODujfOfI9f&FdST=Y-Mz-I)UpY2@B9vpK%26cn;c56L* zcJ(*#dc7D%p!kn93vvIL^Vr~o!@>Lc)eDgwlYmFZT_}Fz47kJXVtSQIN{EVP+;6K> zYw?eBuA)~SMzg~PqtS@cwkFiu+whNRSHnn(s8*@Q@RU9%O3B3QTb5yJpMl6R$3jcm zbAVphnOOxmKX-^=>HMW_Yp%v$reBXwi&w!L4A5X!qHlZ(w$zm2Il89+sr)L1968bP zh|(Hh*6Q)_51(LlO$m12`7CMt>Ju`5bCAwXG2xAe#=uS>{p02){Ohxwc>dn801Y|I|pyh*^Sr!I05ZlKHU44pYZqJjizUmXNX|Ln8R z@XRyMVC2Y=m@;Jwi;j*yY2dnW;X*w4;DgX;G?+1C1_$obE)&Asf+ULoxQT$<5bT0Y z7Dilv#t{oZ<(5N3Y*{HMw(JXINV*2wOWbI0>p* zE7uZggH~&SQq5@mgrOlNHrGIEvqU6K&ft^jVI&#ypHdV)pAR?Oa0AXgcU-rRJAyt2 zqfQK3htnhW>Gc``zDjyXAAK@mzLUY@A&p0;rcbXh`xy+{ZqtEVd=G^}LOAkycAd72 zqXT`iJ>sf&bfNs|+A{8W$SrJc~Jv5u-CA$zZ|bKYWgY#5AUqE3mDpnm{xg9bOmqSR0T}fcU}YmB^Yn zgITn?9t~CB)sBG)X+5qHt8aNgK-e6_On5O)nDVJC;r9YA7&9*@4V5x37Qz{KInsBLvo9S&5`bsOmK zShEI0@?(h&Yw?%2wxEpqa}4d{S*3Uv{CEfSibJjzpB=$@!T%l133xi04k(|1}UUhPUhG{_*x=E3fHccjAuw<_(+L>c09K$?*wr z+DKU}$cH8|A;QGhZ6Jo`qno04qR}Fzs3^h~OG?pISw%8ag_hmB;Bz`bz)iBz!M1MQ zDvrs@%EGZgyS25IapruqW2{yy)9dvlpY{8H-@SXc_#8843^Fq_(b?I_lu9MZX2WR{ zN)mVBkZcYUrs%=}$Zdojwgx0>e4yb6$xl4W>my}O36GLwEdhD6OF_WxAmBcz9>NZv zLXGyY53Nd_=#MfV$$TFd_bLL~M92sSWSqFENN)3&-wwDuJ)Nwft;>X5w}2GixO#z2 za$2#RM_r(q= zdj`GK13&C7Z^El@E`Y^s#N*tSMdiuAeS+Ma6ztetg#m@x7&532o_O*dG&OY~F)0R5 z|K%oRrpMx+|DI2ssS{89{!Z%0J$ds#|GDkxZ2RrE5o~GM|M;O}ux@p{1lxA{HAEeQ zI)Kkgtos~lcK<=X?IVFV4Oq7AX-G-qkGqjLXcy36f9}$Jq#xoB)RRCMnqfv^vJp>z zSBcLzG!yfdi_Ud4c~{#Jks9gI2PPU;wtAROCF8#F9%nV%eauS&NX@4ym=7FZ)9zus zpJv5sdmMkH{cTP^b9zIAqYS1?} z2`ZTan_8+-l$=SR79@e@!HA?R>}sw>UjjjcMS~p#YGs~am0 zw}SZG;@O60<>CCCfdtG7+_vB?ymjHt!n7Qt(jl)~Bd)cz!|U^7U{n(RHs$jD&*3Ev zpOgU1DjXrU!j~SvN6Bm8q5YTi8xAuGSZ?zAvUHOGZq61IbRDBgBhdzhFj{la4I`FV z_cT%nMxn1)bJ0AADVBmn(&Ho(FC(xtrYc(!?G z9r|X(?T;FtII0g`|70~5t=NO#TzUr55~C%RTET*(jNEbcIAo_DuvU3H+X=jbP#o-% z^7ursnw;2VvP*_>sYZ-QWH8bF{%&X-x@g?|ZDT9`HjcnG48-f?n3ZQiyhe%bZN3wJ zj`Df)AR}^um^gY@ei-`pfi^ig(l@tlMSutKa;r>Y94vWx5#X+@fW57a1QwAVTbD5F z3bU)*jU797h`)yqA1*#0c;ErHe*Jo+r>EoD=bppYUwws-KmHgE4Gn@3CMPGeTW+}p zmtTIl#DV|WXP?F5@4kbT6c?>li>XtmvPU0%Q~-QMMP&S>q@>`s+it_^)vE>jzV+5y z*{oT!>_(%}e9D2F29jKg#8XHFfT`LJyjG%wf&{yds7e#&L`+;4Y=gu(FE>2r*?4meY9emKM!58 zF-RRU6zv-}B4Nr|@XVi20@x=2JluVpGARjBgFB$M_{2_$MXcmOw;75-SCXn#4OA_P zppU#;1L)QS95F*+aMu5Pm&>r4@TS*_Ce;V?u5;KE5deJXX7(J+ruySbPUDz@YHO3`#Y! zr!UD9KKAMV+Cuji#?@yev&W|Ah@LWcOACAW%e~^>Ke}lk8<}O6?)`KRTU*(MW?~w= zj?p<5cF$Rv=$9DL&i`jo6&4h?3b5k!@tp<|_B=Rg?(KtFexm+od*LJBeMk((i5rHU z1sgGn()I=nPRv9X?W?nQfFm8F_g+Mddvil2@}iS)MrJxB@oyPP!W{it`_1dMWlZX@M@vw5ySe7bv`=oeuc zm_B(=&~XxLbI>5YZKK(NKkdpLZQ%bw)|4Qn6bXy^H^uI-7L!FlpbB zp63h&m?PkUix_q^-3KWWXr*hW7%bAP{1NQ0s}^7q0k?Ggm@V8e+^j7p!C^M2u;%*; zJakPF)U^Md#T5wh&qd`ppV&7K#NkRI_lN8CD&gCH^@|exW~zxMJIvxUtixsU>$`oS5y-V)}oq#`G<{-Xlrxft7TO7R~e>&kH53{sA|r+*dYrAnEpF|4K4 zfe9lcabnZP=VR%by#(lM@!(C9;U%DJ@A9H=3@KDf6+G1y#IReKHZDQ3tyu-b;K7LL zKM><8-2$-V%qk3wbE?mcUOa4AeSY)`XQcX(4?AceIddAIp6}@Rb zZObRAG2XutMfyExktM?=)59t=AY{yhUK2*Dw*+wnpfMGT;A-Y$_VNQJVZs_`S-3kD zf>_ZTQS#YNk)THb64WiYyMtp2WLn`&=Ca&NK%mGvwo;xYRbh$jKFC$dQ`)W}uMa8G zT}Mv9qNE4P&P+x{WfMxv>hSoJ@8KVRzZDxcl|ZSF!lW|?;icEUMzmQgI`7kexfw2( z2aiAX4#rFvgEi}R3sd=V-%MK@>)g8+tENxG%}@Rbt6zQzeIEHEsz`9HHQI%^joG2<>o$ECovdjX7v=Rj)Q z1aIO5bm-22vHFRhy&r>0!q7A?fo2F76f_~v(ulD8AdyU+QPPf~mwq4?iBE61W^^*P z)YyeKcr?bnkB!T^50G~!wBL6;F<|z)FG{g=PX{LCMd6;aV$sy)!#k@Q&}5^RW>Jee zxDa2ws|`0#NM#8|wKRX5HR6wF5i@-=f&a?AHe5J39=Y*4V!Tda5!l=8#>Y1gWQiuV zu!8cmelv&0V{p1jD4cJtszc+re1}*t~M}zPeqaYNeif7 z{B!+cly|n^w&7=C04W(2%?mx!sIS|s-%<*jY}s+?1q(& z9hH!YHoFZeIuku}5nkJ}6lJYV$c;-nu6E<5?D|$0-v71?RgF$K-2v>Xwqez_W}&q& z+ti3y0#5!;-(FA(qfUwWE347v3J9(H!^P!r(ye_;0IwskWe53*xPp^TOguF~FOo@k zM07YqH1&fYHa3W0v@H6)i@<#4)+QMBN+iVTX$%LTq;m}PowIQbDHWmPoD2T({Eh67 zFMNlBK8d*H%F!?xwOH~~3EuvEEv~+J7&aE?VEUwhTRJva|C~JP(Vzx&|hVTA0Ko0!Tw{#8PA{ zDS@M*357{T|A8c%D!kV>f1qkzFiK&YKv_*>+ zVf^^=$X^BG@v-3{Qj{wQCN_PnHP=6 zgO9#}!9#L!{&}PE@(cIk55IqfI@aA-F#kEM-&i7ocP_hhGFGqHg0}Xqwu-nT1KfWvr){+w)}(NjD>wm=h0L!!g|LeDefCh@$4_z$tu*-M=r{(~8w) zofy-{f`890fJGa5MduEPLo^9gu4M|BJt2MbEzyFUjd9PW?0e$aS{w!`*M%RT@oJlg%+*$*7H zKgAp+&A;OQBVO~Oe#0c%PH6D)>0T}EZfxD%is&c}N^84>kb7y)ZUSP3AZH#=Ktz(i zJ+BnW2?h~_rq@OSCO=(LNvv6e556l$lu5l`5k$AeFNP2^fy-$3bHK1V#EFJX;b@(>}kkczKg zyJ`Q8j2V)RhUN~Ik{B(WHKyRO^KQRtf*+a2;!o7RX97-fI4Dl4-Sh%l3s!93Zn4f zPfZw>pu>I3>TyR=EDBqGCgJthUl;7#@Aspqs0i(JK4;8t zyzvJ1nZA=qf$7_~uLx+{vuBU^eg6FUm@r`iZoc_uj2$}`&p-b>IyyR#k&z){m*PpL z8oK?U7K;V_`t>_K!D%8^iU}oCSfJ@2o-C!lRt{UkXzIg#usr01Mdu_!l@6(+8G}4t z$j!Q*6?OMPd;hb+(%T@5jm3M7W>^TY)gC{r)aK2B0OWm#L2HQtlaajC8KIXSkS&r* z9hq0ZPm1#?m?EV* zyH2d8Gx*JQf57W+EyOzdJta8?ufM%e z7`EHmj$isK%qFx_r}QPJpoxH6PT=G)=`eK074Vbb@~+s0ox4hqa@*YqR_#GkP>$hU zepIaZ4(TH&V8aIskTh!sQi^Uub9E~6i$>9)c7R2z;4GO7fBr0tDJ0Mf(*X0f9mgHX zNiz$PhVMr}eG$Ui1OiJx^&goqv2Zisx7*cl1c)6;;r&cn1Uy6*mN`b?Tf{7V@k=5& zMTkXip57l=(&Qqx8pg!D=>4G0OJQ{EUT70;+2|rreiIo_H-*Y`N z3eJ*DR3``RmzLJz-^*(W{1igC8?|y2*E$cf07U%w`;7@F*zuwXv^#vBNJN542QOXw zrS;z-)e?&vhE5bdVjh)U)7C(vS4+oe_h0zu=ExgVJ$?SJfS>7=s>4odbvsy`&UobD z9haT)1P(FUo`E@?UYCeakI@@=F1-qy_qJnvQ6hfY-Xd7`0pJcpt5)E$vvTn2 z7rU@@Lj#KPV$e*A0#~lHl8x9^)hSHbvnFR@RdF-2X&-0vyUN;e-sn{H&yGT;-G5wZ zQ{SDl0ij@snDybTgo8H!eBr(2tfZnDv!)Hgiyy4Qqj#K*VTI|^AD;h)dAxpN{~k7o zfd9^`CrB29PLge3$!ds=a^TbTAJ{Dv8B}HqHn({2s055l`DOVP0ty0VV!2^bBo$E>sAD2Ncs^g- zVJBG=3c@mA0MaI&8DT}Wb!e}tfl{MEm)nU^1kO5(MO=I5&YeB#=D?es9Z>|{eDh6i zIv0VzoNW&qHcWsyH+SgDgHRcHg0aL<{0LKjijf_#8cK0V z2nw*1glrTGm55B9oN+56LXT^#`{WVpHkpd}I#M_c@uX1vOjv+)dc}xhzeD0szpYj$ z_CNaQb;#|Tiog8*6Ve!CP*9MKDbpVz#vn(R&4EFqMq>N!8jKp*AD@2k2Q2=6BW7H9 zA6nWR$L|k`ijqPjhcYZ%2;29I;Z{g!Ge)5@BMtqOK4P#QXrSpC7@!OohF_S#S2BjA<1^-zZ7$P0as1q++eGVlTnX?lf+q#tJx zK;#9=EcZjMD(bB5uC=R^WMxci9MN?4M2&+mjtmcygZkEgt} zc`5s1$7-CB(GM+78#603_{X%Xr1>RV*uwH{B8t4My_sEvo;)VfZq*e9h?14oe;OVok#Dt8zU)+#9nM6vQ9>WUaMEpj* z)rAfM)=S5wBbJ!vv{9+J{m(1$zWbh(J6(YVLk=4qLII66?Rb4?nCe z74x>^6JyxSd>x8!ydH-B{gL;V=g~56u9&|aHL!@3%pe|k>Py^m`5CzE+@bhr{svNn zL*U~6_M$jxdz*`?Na5Mi?1husEzP9Dy+dL#B3TEEO7)B1mp&sEP9hIlVvRnB6G5+6 z?9aLABG?+6glW9!hU=k?kE}>lLQI%Pk^41jXi`#%O`7!-IY7kR!_J|`wf+v!I)g2d~>t&Eg0 zAAzGxEIh&k#C+PGuBJFeB!#RLmI;ac10b>09z6&LO2y zJ9ibb{SlK){Ec#qGR#46`y3QT6N7C24hnTdA>#_w~H_~KtEj8Daz=UyKH__7_$LxR$*<^f!G4%+x^ z$6EHquJxFjGZ2FlGf>spilUfgG`a2UUuzd(gS847mR~f-AWfAW0=Za6;=(Iz$CUsQ(KQ%^jhSRfGUex9R`Z?jP=f*xE*2P%{N4B z3HR4`*xY#W{h!cA=UjgNV8mDsSX&0h>LuDvx}eOQJQ-`(tV2d}DiMcrY
    j3y#s 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

    {{ instruction }}

    +{% endif %} +
    + {% for key, value in parameters %} + {% if value is iterable %} + {% for val in value %} + + {% endfor %} + {% else %} + + {% endif %} +{% endfor %} + +
    +
    +
    + +
    + +