From 333388ab5c308ccfe033c36f077aae7b00368041 Mon Sep 17 00:00:00 2001 From: Anatoly Cherkasov Date: Mon, 3 Apr 2017 11:38:18 +0300 Subject: [PATCH] ITS-25: uploaded the module (#1) * ITS-25: uploaded the module * ITS-25: added a configuration * ITS-25: added constant * ITS-25: minor changes * ITS-25: minor fix * ITS-25: corrections after the review * ITS-25: fix currency * ITS-25: camelCase * ITS-25: added getHeaders() --- .gitignore | 53 ++++ README.md | 95 +++++- .../controller/payment/rbkmoney_payment.php | 215 +++++++++++++ .../english/payment/rbkmoney_payment.php | 60 ++++ .../russian/payment/rbkmoney_payment.php | 60 ++++ .../view/image/payment/rbkmoney_payment.png | Bin 0 -> 2244 bytes .../template/payment/rbkmoney_payment.tpl | 281 +++++++++++++++++ .../controller/payment/rbkmoney_payment.php | 169 +++++++++++ .../english/payment/rbkmoney_payment.php | 5 + .../russian/payment/rbkmoney_payment.php | 5 + .../model/payment/rbkmoney_payment.php | 287 ++++++++++++++++++ .../template/payment/rbkmoney_payment.tpl | 19 ++ 12 files changed, 1248 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100755 upload/admin/controller/payment/rbkmoney_payment.php create mode 100755 upload/admin/language/english/payment/rbkmoney_payment.php create mode 100755 upload/admin/language/russian/payment/rbkmoney_payment.php create mode 100644 upload/admin/view/image/payment/rbkmoney_payment.png create mode 100644 upload/admin/view/template/payment/rbkmoney_payment.tpl create mode 100755 upload/catalog/controller/payment/rbkmoney_payment.php create mode 100755 upload/catalog/language/english/payment/rbkmoney_payment.php create mode 100755 upload/catalog/language/russian/payment/rbkmoney_payment.php create mode 100755 upload/catalog/model/payment/rbkmoney_payment.php create mode 100755 upload/catalog/view/theme/default/template/payment/rbkmoney_payment.tpl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b3f3fe3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,53 @@ +# Created by .ignore support plugin (hsz.mobi) +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm + +*.DS_Store + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties + +# Target folder +target diff --git a/README.md b/README.md index 3ca0c62..c0cace6 100644 --- a/README.md +++ b/README.md @@ -1 +1,94 @@ -# rbkmoney-cms-opencart \ No newline at end of file +# rbkmoney-cms-opencart + +### Установка и настройка модуля + +#### Установка + +1. Первый способ + +Заархивируйте папку `upload` в `zip` архив и переименуйте его в `rbkmoney-payment.ocmod.zip` +После чего необходимо зайти в `Extension Installer`, нажать `Upload` и выбрать архив для установки: `rbkmoney-payment.ocmod.zip` + +2. Второй способ + +Для установки модуля скопируйте содержимое каталога `upload`: + +``` +/admin/ +/catalog/ +``` + +#### Настройка модуля + +Затем в панели администратора установите и настройте его: + +``` +Extensions > Payments > RBKmoney нажать [Install] +Extensions > Payments > RBKmoney нажать [Edit] и заполнить необходимые настройки +``` + +Для начала приема платежей на Вашем сайте необходимо: + +- Зарегистрироваться на https://dashboard.rbk.money +- Получить необходимые данные для настройки модуля +- Настроить модуль + +#### Совместимость + +- Opencart 2.0 +- Opencart 2.1 +- Opencart 2.2 + +В списке совместимости нет вашей версии Opencart 2.x? Напишите нам обращение - это ускорит процесс адаптации модуля под вашу систему. + + +#### Проблемы и решения + +1. Ошибка: FTP должен быть включен в настройках + +Два варианта решения: + +- Если при установке модуля из админки отображается эта ошибка - вам нужно установить бесплатную FTP QuickFix модификацию localcopy.ocmod.xml. Она установиться без проблем через тот же установщик дополнений, только после установки не забудьте обновить модификации и затем можете приступать к установке любых модулей на Opencart 2. + +- Также решить эту ошибку можно по другому: прописать доступы к FTP в админке Система > Настройки > Магазин > вкладка FTP. + +2. Ошибка: Доступ запрещен! + +Если вы видите сообщение "Доступ запрещен! У Вас нет прав для доступа к этой странице. Если она Вам нужна, обратитесь к администратору." - нужно дать права администраторам на управление модулем или страницей. + +Решение: в админке Opencart 2 переходим в Система > Пользователи > Группы пользователей > Администраторы и здесь нажимаем "Выделить все" ниже обоих блоков, затем Сохранить. + +3. Ошибка: Недопустимый тип файла! + +Если модуль - это один XML файл, то его расширение должно быть .ocmod.xml + +Если модуль - это ocmod.zip архив, то его не нужно распаковывать, а устанавливать как есть. В таком архиве обязательно должна быть папка upload (может быть пустой), а также могут быть файлы модификаций: install.xml, install.php, install.sql. Никаких других файлов в корне архива быть не должно. + +Читайте подробнее как [устанавливать модули в Opencart 2](https://opencart2x.ru/blog/install-module) + + +4. Ошибка: Каталог, содержащий файлы для загрузки не может быть найден! + +Эта ошибка означает, что в загружаемом архиве отсутсвует папка upload. Даже если у модуля нет файлов, кроме модификаций - эта папка должна присутствовать в архиве модуля .ocmod.zip, тогда она должна оставаться пустой. + +5. Ошибка: Модификатор использует тот же ID код который вы пытаетесь загрузить! + +Эта ошибка означает, что вы пытаетесь установить модификатор, который уже установлен или, возможно, у какого-то вашего модуля такой же ID. + +Для решения этой ошибки вам нужно перед установкой удалить старую версию модификации в разделе Модули > Модификации. + +Если такого модуля у вас нет, но совпадает ID, тогда нужно поменять значение параметра `` в устанавливаемом модификаторе XML, сделать этот параметр уникальным дописав несколько символов. + +6. Ошибка: `Warning: DOMDocument::loadXML(): CData section not finished` + +Эта ошибка означает, что вы пытаетесь установить слишком объемный xml-модификатор. + +Количество символов в `ocmod.xml` файле не должно превышать 65535. + +Для решения ошибки нужно разбить xml-файл модификации на несколько частей, главное - не забыть задавать каждой уникальное значение в ``, можно добавлять к текущему значению цифры 1,2,3... как идентификаторы части. + +Еще одним способом решения есть изменения типа в поля, где храняться модификации, в таблице `'oc_modification'` базы данных. Нужно выполнить следующий SQL-запрос: + +``` +ALTER TABLE oc_modification CHANGE xml xml MEDIUMTEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ; +``` diff --git a/upload/admin/controller/payment/rbkmoney_payment.php b/upload/admin/controller/payment/rbkmoney_payment.php new file mode 100755 index 0000000..4000326 --- /dev/null +++ b/upload/admin/controller/payment/rbkmoney_payment.php @@ -0,0 +1,215 @@ + array( + 'heading_title', + 'text_edit', + 'text_yes', + 'text_no', + + 'button_save', + 'button_cancel', + + // Enable/Disable module + 'entry_status', + 'help_status', + 'text_enabled', + 'text_disabled', + + // Sort order module + 'entry_sort_order', + 'help_sort_order', + + // Enable logs for module + 'entry_logs', + + // Geo Zone + 'text_all_zones', + 'entry_geo_zone', + 'help_geo_zone', + + // Other parameters + 'entry_order_status', + 'help_order_status', + + 'entry_shop_id', + 'help_shop_id', + + 'entry_form_path_logo', + 'help_form_path_logo', + + 'entry_form_company_name', + 'help_form_company_name', + + 'entry_private_key', + 'help_private_key', + + 'entry_callback_public_key', + 'help_callback_public_key', + + 'entry_currency', + 'help_currency', + + 'entry_order_status_progress', + 'help_order_status_progress', + + 'entry_notify_url', + 'help_notify_url', + ), + 'fields' => array( + 'rbkmoney_payment_status', + 'rbkmoney_payment_sort_order', + 'rbkmoney_payment_geo_zone_id', + 'rbkmoney_payment_logs', + 'rbkmoney_payment_order_status_id', + 'rbkmoney_payment_order_status_progress_id', + 'rbkmoney_payment_form_path_logo', + 'rbkmoney_payment_form_company_name', + 'rbkmoney_payment_shop_id', + 'rbkmoney_payment_private_key', + 'rbkmoney_payment_callback_public_key', + ), + 'errors' => array( + 'error_shop_id', + 'error_private_key', + 'error_callback_public_key', + ), + 'validate' => array( + array( + 'field' => 'rbkmoney_payment_shop_id', + 'error_name' => 'error_shop_id', + ), + array( + 'field' => 'rbkmoney_payment_private_key', + 'error_name' => 'error_private_key', + ), + array( + 'field' => 'rbkmoney_payment_callback_public_key', + 'error_name' => 'error_callback_public_key', + ), + ), + ); + + public function index() + { + $data['rbkmoney_payment_version'] = '1.1 for OpenCart 2.x'; + + $this->load->language('payment/rbkmoney_payment'); + + $this->document->setTitle($this->language->get('heading_title')); + + $this->load->model('setting/setting'); + $this->load->model('localisation/geo_zone'); + $data['geo_zones'] = $this->model_localisation_geo_zone->getGeoZones(); + + if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) { + $this->model_setting_setting->editSetting('rbkmoney_payment', $this->request->post); + + $this->session->data['success'] = $this->language->get('text_success'); + + $this->response->redirect($this->prepareUrlLink('extension/payment')); + } + + $data['error_warning'] = (isset($this->error['warning'])) ? $this->error['warning'] : ''; + + $data['breadcrumbs'] = [ + array( + 'text' => $this->language->get('text_home'), + 'href' => $this->prepareUrlLink('common/dashboard') + ), + array( + 'text' => $this->language->get('text_payment'), + 'href' => $this->prepareUrlLink('extension/payment') + ), + array( + 'text' => $this->language->get('heading_title'), + 'href' => $this->prepareUrlLink('payment/rbkmoney_payment') + ) + ]; + + $this->settings['buttons'] = array( + 'action' => $this->prepareUrlLink('payment/rbkmoney_payment'), + 'cancel' => $this->prepareUrlLink('extension/payment'), + ); + + foreach ($this->settings['errors'] as $error) { + $data[$error] = $this->getErrorByName($error); + } + + foreach ($this->settings['buttons'] as $name => $value) { + $data[$name] = $this->language->get($value); + } + + foreach ($this->settings['common_parameters'] as $common_parameter) { + $data[$common_parameter] = $this->language->get($common_parameter); + } + + foreach ($this->settings['fields'] as $field) { + $data[$field] = $this->getConfigByField($field); + } + + $this->load->model('localisation/order_status'); + $data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses(); + + $data['notify_url'] = $this->getNotifyUrl(); + + $data['header'] = $this->load->controller('common/header'); + $data['column_left'] = $this->load->controller('common/column_left'); + $data['footer'] = $this->load->controller('common/footer'); + + $this->response->setOutput($this->load->view('payment/rbkmoney_payment', $data)); + } + + /** + * Validate parameters + * + * @return bool + */ + protected function validate() + { + if (!$this->user->hasPermission('modify', 'payment/rbkmoney_payment')) { + $this->error['warning'] = $this->language->get('error_permission'); + } + + foreach ($this->settings['validate'] as $validate) { + if (!$this->request->post[$validate['field']]) { + $this->error[$validate['error_name']] = $this->language->get($validate['error_name']); + } + } + + return !$this->error; + } + + private function getNotifyUrl() + { + return HTTPS_CATALOG . 'index.php?route=payment/rbkmoney_payment/notify'; + } + + private function getConfigByField($fieldName) + { + return (isset($this->request->post[$fieldName])) + ? $this->request->post[$fieldName] + : $this->config->get($fieldName); + } + + private function prepareUrlLink($link) + { + return $this->url->link($link, 'token=' . $this->session->data['token'], 'SSL'); + } + + + private function getErrorByName($name, $default_message = '') + { + return (isset($this->error[$name])) ? $this->error[$name] : $default_message; + } +} + +?> diff --git a/upload/admin/language/english/payment/rbkmoney_payment.php b/upload/admin/language/english/payment/rbkmoney_payment.php new file mode 100755 index 0000000..044e9eb --- /dev/null +++ b/upload/admin/language/english/payment/rbkmoney_payment.php @@ -0,0 +1,60 @@ + RBKmoney '; + +$_['text_yes'] = 'Yes'; +$_['text_no'] = 'No'; +$_['entry_logs'] = 'Enable logs'; + + +// Common settings +$_['entry_status'] = 'Status:'; +$_['help_status'] = 'Status'; + +$_['entry_sort_order'] = 'Sort order:'; +$_['help_sort_order'] = 'Sort order'; + +$_['entry_geo_zone'] = 'Geographical area'; +$_['help_geo_zone'] = 'Geographical area'; + + +// RBKmoney settings +$_['entry_shop_id'] = 'Shop ID:'; +$_['help_shop_id'] = 'Number of the merchant\'s shop system RBKmoney'; +$_['error_shop_id'] = 'Shop ID is required'; + +$_['entry_order_status'] = 'Order status after the payment'; +$_['help_order_status'] = 'Order status after the payment.'; + +$_['entry_order_status_progress'] = 'Order Status after invoicing:'; +$_['help_order_status_progress'] = 'Order Status after invoicing.'; + +$_['entry_form_path_logo'] = 'Logo in payment form:'; +$_['help_form_path_logo'] = 'Your logo for payment form'; + +$_['entry_form_company_name'] = 'Company name in payment form:'; +$_['help_form_company_name'] = 'Your company name for payment form'; + +$_['entry_private_key'] = 'Private key:'; +$_['help_private_key'] = 'The private key in the system RBK Money.'; +$_['error_private_key'] = 'Private key is required'; + +$_['entry_callback_public_key'] = 'Callback public key:'; +$_['help_callback_public_key'] = 'Callback public key'; +$_['error_callback_public_key'] = 'Callback public key is required'; + +$_['entry_notify_url'] = 'Notification URL:'; +$_['help_notify_url'] = 'This address is to be inserted in a private office RBK Money'; + +// Common error +$_['error_permission'] = 'You are not allowed to control this unit'; + +?> diff --git a/upload/admin/language/russian/payment/rbkmoney_payment.php b/upload/admin/language/russian/payment/rbkmoney_payment.php new file mode 100755 index 0000000..4b69c5e --- /dev/null +++ b/upload/admin/language/russian/payment/rbkmoney_payment.php @@ -0,0 +1,60 @@ + RBKmoney '; + +$_['text_yes'] = 'Да'; +$_['text_no'] = 'Нет'; +$_['entry_logs'] = 'Включить логи'; + + +// Common settings +$_['entry_status'] = 'Статус:'; +$_['help_status'] = 'Статус'; + +$_['entry_sort_order'] = 'Sort order:'; +$_['help_sort_order'] = 'Sort order'; + +$_['entry_geo_zone'] = 'Географическая зона'; +$_['help_geo_zone'] = 'Географическая зона'; + + +// RBKmoney settings +$_['entry_shop_id'] = 'Shop ID:'; +$_['help_shop_id'] = 'Номер магазина мерчанта в системе RBKmoney'; +$_['error_shop_id'] = 'Shop ID обязателен'; + +$_['entry_order_status'] = 'Статус заказа после оплаты:'; +$_['help_order_status'] = 'Статус заказа после оплаты'; + +$_['entry_order_status_progress'] = 'Статус заказа после выставления счета:'; +$_['help_order_status_progress'] = 'Статус заказа после выставления счета'; + +$_['entry_form_path_logo'] = 'Логотип в платежной форме:'; +$_['help_form_path_logo'] = 'Путь к логотипу отображаемого в платежной форме'; + +$_['entry_form_company_name'] = 'Название компании в платежной форме:'; +$_['help_form_company_name'] = 'Название компании отображаемой в платежной форме'; + +$_['entry_private_key'] = 'Приватный ключ:'; +$_['help_private_key'] = 'Приватный ключ в системе RBKmoney'; +$_['error_private_key'] = 'Приватный ключ обязателен'; + +$_['entry_callback_public_key'] = 'Публичный ключ для обработчика уведомления:'; +$_['help_callback_public_key'] = 'Публичный ключ для обработчика уведомления в системе RBKmoney'; +$_['error_callback_public_key'] = 'Публичный ключ для обработчика уведомления обязателен'; + +$_['entry_notify_url'] = 'URL для оповещения о платеже:'; +$_['help_notify_url'] = 'Этот адрес необходимо будет добавить в настройках личного кабинета в системе RBKmoney'; + +// Common error +$_['error_permission'] = 'У Вас нет прав для управления этим модулем!'; + +?> \ No newline at end of file diff --git a/upload/admin/view/image/payment/rbkmoney_payment.png b/upload/admin/view/image/payment/rbkmoney_payment.png new file mode 100644 index 0000000000000000000000000000000000000000..b67e4fd3b0070f34717eb8190b2f56e010a2e7f2 GIT binary patch literal 2244 zcmeHI`8V5%9!-m?t(I0Kl^2SJ5?kq%)T7a)k%=myDt4mi(AJV^zLFKTd6Oz7Gc zsHyf3=l6L1Q1O0z z`|udFmrM5mXTvWKr_qheg6RWP_!L?z1IM1B<4Q5|*bI4vP)Vz<-w34nX(xD?E@|DE zp%2MUZVifsR0;jQUIKhE0f@N0m`HV68SICvjRcm=6O%WnIXS!ew|5I^A2Q>a^+@n zqh(u;xd-CS1A)8wiiwI!0JE`Yh}c=be$v#oB(sMzU9X@_>oWWzI~5hzsxW$;76U-x zb*dL{WLkCaRn~g0cb82vKtyi>7 zo?CH1%Z76kC>OGTiPylvtQJ7~@|e`~6a+|d7Z+R#eV*Qx3MDrokw2QdcyYrkqd%N) zJ(T2`He2kO7U0Edlm3vp{xt+DDlFq#1^TWT~@yg253+ge^I% z^4xG~hmC*B@v*3;tl9NGDCY-`yM2cD@XNeE1!`i;2a?6B??N9f{YZ@|3cKvQ zW7I8yDSZ%X($k(iLIK-F&ZRkJ3@R3aWIPlPBkwq92Tv4pghhKF%K}Dn74La&feAW# z2DCMBLuyW0b8)!!nQ#%4967J5+Y!#du`^lD=67oAdDSh2Oj4i^`T&@23D@00S~yC- z#?+*^r5q`r0~n@r&xTkJf)FHP!j45|m}wV$rW64I&&qokOx1XOcPS0S7ZWJVcH*oQ z@IqCq@En>|{h+zgJQiOF#&y)#WB7S0v6KwoC{PwD=bE+ptvvjuT%1pk)m2_ee8m+j z1yMsFf4=d=fV42!`@TIj5jV?Q|EYb|==G_J?POAz*bH3lL-;xKufW>4R!y#c`oUGf zz33u4Y(;@lYNH6Suj=s~u3{lky~H+!^didRdxK2>mS|ouTO7=}yI3W5;Cd@YsLI^@ zXaQ$*2pWW={Tf|w5dTttVG1u^z@4}`Y!YPPmn|=tvgqXIHf+vEY4STe>!Dzd>$=+=-_pWJk*#uK%{bIi0?n-a*7Jv0 z1f#nG`7B|HGztL=Q>AB*^@kR+#-|~u!Cljpho#v8+~!Y5-lurDysHsG{RQU8f2|1|;Z0`9){xa9PtMf5#fYJ+<5EDS{b)};P3s_Q$*AkyMvdp#Jc#;pp z^F)Y_L;Z8sv`NNT)O(o*&17=Zs#@`d3*-_k>F}W-W%`aFqH#1+TjnTbX$)Nl+olTh z*Aj2IENR_@vyw`JL=B*-k=_{u9m0>*SzdUnI6<1if9~tFK`E6)xuCns1hKB&qRDd0 z$owYSCL%4#5nFfqT4V@wXIuw+Jy!44uZohsZ_tKGA0c!oPY|&{{c_G}!otbdokzNC z^L6Zw@+~H#IidKo;8~Y~aUHnajSZbc6HkY2>*?_2-mSjeJdaP(oqlcY=<_D#0vu+H g!$|DTF5NAb$<0EV)+kcq^#6JWk9EV;ItMfV2CdWbqyPW_ literal 0 HcmV?d00001 diff --git a/upload/admin/view/template/payment/rbkmoney_payment.tpl b/upload/admin/view/template/payment/rbkmoney_payment.tpl new file mode 100644 index 0000000..92bce44 --- /dev/null +++ b/upload/admin/view/template/payment/rbkmoney_payment.tpl @@ -0,0 +1,281 @@ + +
+ + +
+ +
+ +
+ + +
+
+

+
+
+
+ + + + + +
+ + RBKmoney + +
+ +
+ +
+ +
+ + +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ + +
+ +
+
+ + +
+ +
+ + +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ + +
+
+ + +
+
+
RBKmoney v
+
+
+
+
+ \ No newline at end of file diff --git a/upload/catalog/controller/payment/rbkmoney_payment.php b/upload/catalog/controller/payment/rbkmoney_payment.php new file mode 100755 index 0000000..57851c0 --- /dev/null +++ b/upload/catalog/controller/payment/rbkmoney_payment.php @@ -0,0 +1,169 @@ +language->get('button_confirm'); + $data['button_back'] = $this->language->get('button_back'); + + $this->load->language('payment/rbkmoney_payment'); + $this->load->model('checkout/order'); + $this->load->model('payment/rbkmoney_payment'); + + $data['action'] = static::CHECKOUT_URL; + $data['shop_id'] = $this->config->get('rbkmoney_payment_shop_id'); + $data['form_path_logo'] = $this->config->get('rbkmoney_payment_form_path_logo'); + $data['form_company_name'] = $this->config->get('rbkmoney_payment_form_company_name'); + $data['private_key'] = $this->config->get('rbkmoney_payment_private_key'); + $data['success_redirect_url'] = $this->url->link('payment/rbkmoney_payment/success_url_redirect'); + $data['failed_redirect_url'] = $this->url->link('payment/rbkmoney_payment/failed_url_redirect'); + + $order_info = $this->model_checkout_order->getOrder($this->session->data['order_id']); + $data['amount'] = number_format($order_info['total'], 2, '.', ''); + $data['currency'] = $order_info['currency_code']; + $data['order_id'] = $this->session->data['order_id']; + + $invoiceId = ''; + $invoice_access_token = ''; + + try { + $invoiceId = $this->model_payment_rbkmoney_payment->createInvoice($order_info); + $invoice_access_token = $this->model_payment_rbkmoney_payment->createAccessToken($invoiceId); + + $this->model_checkout_order->addOrderHistory( + $this->session->data['order_id'], + $this->config->get('rbkmoney_payment_order_status_progress_id') + ); + } catch (Exception $ex) { + $logs = array(); + $logs['error']['message'] = $ex->getMessage(); + $data['errormsg'] = $ex->getMessage(); + $this->model_payment_rbkmoney_payment->logger('exception', $logs); + } + + $data['invoice_id'] = $invoiceId; + $data['invoice_access_token'] = $invoice_access_token; + + return $this->load->view('payment/rbkmoney_payment', $data); + } + + /** + * http{s}://{your-site}/index.php?route=payment/rbkmoney_payment/success_url_redirect + */ + public function success_url_redirect() + { + header('Location: ' . $this->url->link('checkout/success'), true, 301); + exit(); + } + + /** + * http{s}://{your-site}/index.php?route=payment/rbkmoney_payment/failed_url_redirect + */ + public function failed_url_redirect() + { + header('Location: ' . $this->url->link('checkout/payment'), true, 301); + exit(); + } + + /** + * http{s}://{your-site}/index.php?route=payment/rbkmoney_payment/callback + */ + public function callback() + { + $body = file_get_contents('php://input'); + $logs = array( + 'request' => array( + 'method' => 'POST', + 'data' => $body, + ), + ); + + $method = 'notification'; + $this->load->model('payment/rbkmoney_payment'); + $this->model_payment_rbkmoney_payment->logger($method, $logs); + + if (empty($_SERVER[static::SIGNATURE])) { + $logs['error']['message'] = 'Сигнатура отсутствует'; + $this->outputWithLogger($method, $logs); + } + + $required_fields = array( + static::INVOICE_ID, + static::PAYMENT_ID, + static::AMOUNT, + static::CURRENCY, + static::CREATED_AT, + static::METADATA, + static::STATUS + ); + $data = json_decode($body, TRUE); + + foreach ($required_fields as $field) { + if (empty($data[$field])) { + $logs['error']['message'] = 'Отсутствует обязательное поле'; + $this->outputWithLogger($method, $logs); + } + } + + if (empty($data[static::METADATA][static::ORDER_ID])) { + $logs['error']['message'] = 'Отсутствует номер заказа'; + $this->outputWithLogger($method, $logs); + } + + $signature = base64_decode($_SERVER[static::SIGNATURE]); + $public_key = $this->config->get('rbkmoney_payment_callback_public_key'); + if (!$this->model_payment_rbkmoney_payment->verification_signature($body, $signature, $public_key)) { + $logs['error']['message'] = 'Сигнатура не совпадает'; + $this->outputWithLogger($method, $logs); + } + + $orderId = $data[static::METADATA][static::ORDER_ID]; + if (!$order_info = $this->model_checkout_order->getOrder($orderId)) { + $logs['error']['message'] = 'Заказ ' . $orderId . ' не найден'; + $this->outputWithLogger($method, $logs); + } + + if ($order_info['order_status_id'] == 0) { + $this->model_checkout_order->addOrderHistory($order_info['order_id'], $this->config->get('rbkmoney_payment_order_status_id'), 'RBKmoney'); + $logs['order_info'] = $order_info; + $this->outputWithLogger($method, $logs, self::HEADER_OK); + } + + if (($data[static::STATUS] == 'paid') && ($order_info['order_status_id'] != $this->config->get('rbkmoney_payment_order_status_id'))) { + $this->model_checkout_order->addOrderHistory($order_info['order_id'], $this->config->get('rbkmoney_payment_order_status_id'), 'RBKmoney', TRUE); + $logs['order_info'] = $order_info; + $this->outputWithLogger($method, $logs, self::HEADER_OK); + } else { + $logs['error']['message'] = 'Заказ ' . $orderId . ' уже имеет финальный статус'; + $this->outputWithLogger($method, $logs); + } + + } + + private function outputWithLogger($method, &$logs, $header = self::HEADER_BAD_REQUEST) + { + $this->load->model('payment/rbkmoney_payment'); + $this->model_payment_rbkmoney_payment->logger($method, $logs); + $this->response->setOutput(header($header)); + exit(); + } + +} diff --git a/upload/catalog/language/english/payment/rbkmoney_payment.php b/upload/catalog/language/english/payment/rbkmoney_payment.php new file mode 100755 index 0000000..b7f80d8 --- /dev/null +++ b/upload/catalog/language/english/payment/rbkmoney_payment.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/upload/catalog/language/russian/payment/rbkmoney_payment.php b/upload/catalog/language/russian/payment/rbkmoney_payment.php new file mode 100755 index 0000000..d8c07f1 --- /dev/null +++ b/upload/catalog/language/russian/payment/rbkmoney_payment.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/upload/catalog/model/payment/rbkmoney_payment.php b/upload/catalog/model/payment/rbkmoney_payment.php new file mode 100755 index 0000000..2fd29f5 --- /dev/null +++ b/upload/catalog/model/payment/rbkmoney_payment.php @@ -0,0 +1,287 @@ +load->language('payment/rbkmoney'); + + if ($this->config->get('rbkmoney_payment_status')) { + + $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "zone_to_geo_zone WHERE geo_zone_id = '" . (int)$this->config->get('rbkmoney_payment_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('rbkmoney_payment_geo_zone_id')) { + $status = TRUE; + } elseif ($query->num_rows) { + $status = TRUE; + } else { + $status = FALSE; + } + } else { + $status = FALSE; + } + + $method_data = array(); + + if ($status) { + $method_data = array( + 'code' => 'rbkmoney_payment', + 'title' => $this->language->get('text_title'), + 'terms' => '', + 'sort_order' => $this->config->get('rbkmoney_payment_sort_order') + ); + } + + return $method_data; + } + + /** + * Create a new invoice. + * + * @param array $order_info + * @return mixed + */ + public function createInvoice(array $order_info) + { + $headers = $this->getHeaders(); + + $data = [ + 'shopID' => (int)$this->config->get('rbkmoney_payment_shop_id'), + 'amount' => $this->prepareAmount($order_info['total']), + 'metadata' => $this->prepareMetadata($order_info['order_id']), + 'dueDate' => $this->prepareDueDate(), + 'currency' => strtoupper($order_info['currency_code']), + 'product' => $order_info['order_id'], + 'description' => $this->getProductDescription(), + ]; + + $url = $this->prepareApiUrl('processing/invoices'); + + $response = $this->send($url, 'POST', $headers, json_encode($data, true), 'init_invoice'); + $invoice_encode = json_decode($response['body'], true); + + return (!empty($invoice_encode['id'])) ? $invoice_encode['id'] : ''; + } + + private function getHeaders() { + $headers = array(); + $headers[] = 'X-Request-ID: ' . uniqid(); + $headers[] = 'Authorization: Bearer ' . $this->config->get('rbkmoney_payment_private_key'); + $headers[] = 'Content-type: application/json; charset=utf-8'; + $headers[] = 'Accept: application/json'; + return $headers; + } + + /** + * Get product descriptions from the shopping cart + * + * @return string + */ + private function getProductDescription() + { + $products = ''; + + $i = 0; + foreach ($this->cart->getProducts() as $product) { + if ($i == 0) + $products .= $product['quantity'] . ' x ' . $product['name']; + else + $products .= ', ' . $product['quantity'] . ' x ' . $product['name']; + + $i++; + } + + if (mb_strlen($products, 'UTF-8') > 255) { + $products = mb_substr($products, 0, 252, 'UTF-8') . '...'; + } + + return $products; + } + + /** + * Create a new token to access the specified invoice. + * + * @param $invoice_id + * @return string + * @throws Exception + */ + public function createAccessToken($invoice_id) + { + if (empty($invoice_id)) { + throw new Exception('Не передан обязательный параметр invoice_id'); + } + $headers = $this->getHeaders(); + + $url = $this->prepareApiUrl('processing/invoices/' . $invoice_id . '/access_tokens'); + + $response = $this->send($url, 'POST', $headers, '', 'access_tokens'); + if ($response['http_code'] != 201) { + throw new Exception('Возникла ошибка при создании токена для инвойса'); + } + $response_decode = json_decode($response['body'], true); + $access_token = !empty($response_decode['payload']) ? $response_decode['payload'] : ''; + return $access_token; + } + + /** + * Send request + * + * @param $url + * @param $method + * @param array $headers + * @param string $data + * @param string $type + * @return mixed + * @throws Exception + */ + private function send($url, $method, $headers = [], $data = '', $type = '') + { + $logs = array( + 'request' => array( + 'url' => $url, + 'method' => $method, + 'headers' => $headers, + 'data' => $data, + ), + ); + $this->logger($type . ': request', $logs); + + if (empty($url)) { + throw new Exception('Не передан обязательный параметр url'); + } + + $allowed_methods = ['POST']; + if (!in_array($method, $allowed_methods)) { + $this->logger(__CLASS__, $logs); + throw new Exception('Unsupported method ' . $method); + } + + $curl = curl_init($url); + curl_setopt($curl, CURLOPT_POST, TRUE); + curl_setopt($curl, CURLOPT_POSTFIELDS, $data); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + + $body = curl_exec($curl); + $info = curl_getinfo($curl); + $curl_errno = curl_errno($curl); + + $response['http_code'] = $info['http_code']; + $response['body'] = $body; + $response['error'] = $curl_errno; + + $logs['response'] = $response; + + $this->logger($type . ': response', $logs); + + curl_close($curl); + + return $response; + } + + /** + * Prepare due date + * + * @return string + */ + private function prepareDueDate() + { + date_default_timezone_set('UTC'); + return date(static::CREATE_INVOICE_TEMPLATE_DUE_DATE, strtotime(static::CREATE_INVOICE_DUE_DATE)); + } + + /** + * Prepare metadata + * + * @param $order_id + * @return array + */ + private function prepareMetadata($order_id) + { + return [ + 'cms' => 'opencart', + 'cms_version' => VERSION, + 'module' => 'rbkmoney_payment', + 'order_id' => $order_id, + ]; + } + + /** + * Prepare amount (e.g. 124.24 -> 12424) + * + * @param $amount int + * @return int + */ + private function prepareAmount($amount) + { + return number_format($amount, 2, '.', '') * 100; + } + + /** + * Prepare API URL + * + * @param string $path + * @param array $query_params + * @return string + */ + private function prepareApiUrl($path = '', $query_params = []) + { + $url = rtrim($this->api_url, '/') . '/' . $path; + if (!empty($query_params)) { + $url .= '?' . http_build_query($query_params); + } + return $url; + } + + /** + * Verification signature + * + * @param $data + * @param $signature + * @param $public_key + * @return bool + */ + public function verificationSignature($data, $signature, $public_key) + { + if (empty($data) || empty($signature) || empty($public_key)) { + return FALSE; + } + $public_key_id = openssl_get_publickey($public_key); + if (empty($public_key_id)) { + return FALSE; + } + $verify = openssl_verify($data, $signature, $public_key_id, OPENSSL_ALGO_SHA256); + return ($verify == static::OPENSSL_VERIFY_SIGNATURE_IS_CORRECT); + } + + /** + * Data logging + * + * @param $method + * @param $message + */ + public function logger($method, $message) + { + if ($this->config->get('rbkmoney_payment_logs')) { + $this->log->write('rbkmoney ' . $method . '. ' . print_r($message, true)); + } + } + +} diff --git a/upload/catalog/view/theme/default/template/payment/rbkmoney_payment.tpl b/upload/catalog/view/theme/default/template/payment/rbkmoney_payment.tpl new file mode 100755 index 0000000..b1318d7 --- /dev/null +++ b/upload/catalog/view/theme/default/template/payment/rbkmoney_payment.tpl @@ -0,0 +1,19 @@ + + + + +