diff --git a/CHANGELOG.md b/CHANGELOG.md index 856bef7..a2674d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +### v2.3.1 от 01.02.2022 +* Добавлена обработка уведомлений от юкассы: PAYMENT_CANCELED, REFUND_SUCCEEDED, DEAL_CLOSED, PAYOUT_CANCELED, +PAYOUT_SUCCEEDED. +* Обноление SDK до версии 2.2.5 +* Обновление верстки форм в админке с использованием bootstrap 5 +* Добавлена запись в историю заказа о сумме возврата +* Добавлена проверка и сохранение данных о полученных уведомлениях о возвратах +* Исправлена обработка уведомлений от от юкассы для модуля оплаты через сббол + ### v2.3.0 от 24.12.2021 * Добавлена поддержка версий Joomla! 4.x и Joomshopping 5.x * Обновлен SDK до версии 2.2.4 diff --git a/src/components/com_jshopping/payments/pm_yoomoney/4x/yookassa.php b/src/components/com_jshopping/payments/pm_yoomoney/4x/yookassa.php index 3fc6738..776a712 100644 --- a/src/components/com_jshopping/payments/pm_yoomoney/4x/yookassa.php +++ b/src/components/com_jshopping/payments/pm_yoomoney/4x/yookassa.php @@ -39,186 +39,205 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+
- + +
+
+ /> -
-

-
- - - -

-
- - -

-
-

-
- - - />
-
-
- /> -

-
-

- -
- -
-
- /> - -
-
- -
- - -

-
- - + + + + +
+
+ +
+ +
+ +
+
+
+ +
+
+ +
+
+
+ /> - - - - - - - - - - - - - -
-

-
- - - getAllOrderStatus(), - 'pm_params[yookassa_hold_mode_on_hold_status]', - 'class="form-select form-control form-select-sm" size="1" data-type="kassa"', 'status_id', - 'name', $params['yookassa_hold_mode_on_hold_status']); ?> -
- - - getAllOrderStatus(), - 'pm_params[yookassa_hold_mode_cancel_status]', - 'class="form-select form-control form-select-sm" size="1" data-type="kassa"', 'status_id', - 'name', $params['yookassa_hold_mode_cancel_status']); ?> -
-
- - />
- /> -
-
- +
+
+ + > + +
+
+
+
+ +
+
-

-

- - - - - - $tax) { ?> - - - - - -
- - - -
- - -
- -
-
+
- -
-
+

:

+
+
+ : +
+
+ : +
+
+ $tax) { ?> +
+
+ +
+
+ +
+
+ +
+
- - + -

+
- -
-
+
+
- $value): ?> @@ -301,11 +321,13 @@ class="">%
- -
-
- - $value): ?> @@ -313,12 +335,14 @@ class="">%
- -
-
+
+
- $value): ?> @@ -326,11 +350,15 @@ class="">%
- -
-
- - $value): ?> @@ -338,103 +366,109 @@ class="">%
- -
-
- - - +
+
-
-
- -
+
+
+
+ + type="radio" value="1" id="send_second_receipt-on" + > +
-
-
-
- - - - - - - -
- - - getAllOrderStatus(), - 'pm_params[kassa_second_receipt_status]', - 'class="form-select form-control form-select-sm" size="1" data-type="kassa"', 'status_id', 'name', - $params['kassa_second_receipt_status']); - ?> -
-
-
-

- -

+
+ + type="radio" value="0" id="send_second_receipt-off" + > +
-
- - -
- +
+
+
+ +
+
+ getAllOrderStatus(), + 'pm_params[kassa_second_receipt_status]', + 'class="form-select form-control form-select-sm" size="1" data-type="kassa"', 'status_id', 'name', + $params['kassa_second_receipt_status']); + ?> +
+
-

-
-

-
- - - getAllOrderStatus(), - 'pm_params[kassa_transaction_end_status]', - 'class="transaction-end-status form-select form-control form-select-sm" size="1" data-type="kassa"', 'status_id', 'name', - $params['kassa_transaction_end_status']); - ?> -
- - - + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+

+
+
+ +
+
+ +
+ +
+ getAllOrderStatus(), + 'pm_params[kassa_transaction_end_status]', + 'class="transaction-end-status form-select form-control form-select-sm" size="1" data-type="kassa"', 'status_id', 'name', + $params['kassa_transaction_end_status']); + ?> +
+
+
+
+ +
+
+ +
-
+ + + + +
+
+

+
+
+
+
+ +
+
+ getAllOrderStatus(), 'pm_params[money_transaction_end_status]', + 'class="transaction-end-status form-select form-control form-select-sm" size="1" data-type="money"', + 'status_id', 'name', $params['money_transaction_end_status'] ); + ?> +
+
\ No newline at end of file diff --git a/src/components/com_jshopping/payments/pm_yoomoney/adminparamsform.php b/src/components/com_jshopping/payments/pm_yoomoney/adminparamsform.php index 6394666..a74f480 100644 --- a/src/components/com_jshopping/payments/pm_yoomoney/adminparamsform.php +++ b/src/components/com_jshopping/payments/pm_yoomoney/adminparamsform.php @@ -87,9 +87,9 @@ function toggleShowSecondReceipt(isShow) function taxes_validate_mode(paymode) { jQuery(function($) { if (paymode == 1) { - $(".taxesArea").show(); + $(".taxesArea").slideDown(); } else { - $(".taxesArea").hide(); + $(".taxesArea").slideUp(); } }); } @@ -174,7 +174,9 @@ function installWidgetHandler() { success: function (data) { if (data['is_success'] !== true) { jQuery('#warning_for_verify_file_install').html(data['message']).slideDown(); + return false; } + jQuery('#success_for_verify_file_install').html(data['message']).slideDown(); } }) } diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lang/en-GB.php b/src/components/com_jshopping/payments/pm_yoomoney/lang/en-GB.php index a5fc0f3..9082dd7 100644 --- a/src/components/com_jshopping/payments/pm_yoomoney/lang/en-GB.php +++ b/src/components/com_jshopping/payments/pm_yoomoney/lang/en-GB.php @@ -228,4 +228,5 @@ define('_JSHOP_YOO_BTN_BACK', 'Back'); define('_JSHOP_REDIRECT_TO_PAYMENT_PAGE', 'Redirecting to the Payment page, please wait...'); -define('_JSHOP_PAYMENT_NUMBER', 'Order number %s'); \ No newline at end of file +define('_JSHOP_PAYMENT_NUMBER', 'Order number %s'); +define('_JSHOP_YOO_KASSA_REFUND_SUCCEDED_ORDER_HISTORY', 'Refund was made. Amount is %s ₽.'); \ No newline at end of file diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lang/ru-RU.php b/src/components/com_jshopping/payments/pm_yoomoney/lang/ru-RU.php index 76ca236..670ab4f 100644 --- a/src/components/com_jshopping/payments/pm_yoomoney/lang/ru-RU.php +++ b/src/components/com_jshopping/payments/pm_yoomoney/lang/ru-RU.php @@ -60,14 +60,14 @@ define('_JSHOP_YOO_KASSA_PAYMODE_LINK', 'Подробнее о сценариях оплаты'); define('_JSHOP_YOO_KASSA_SELECT_TEXT', 'Отметьте способы оплаты, которые указаны в вашем договоре с ЮMoney'); define('_JSHOP_YOO_KASSA_CREDENTIALS_ERROR', 'Проверьте shopId и Секретный ключ — где-то есть ошибка. А лучше скопируйте их прямо из личного кабинета ЮKassa'); -define('_JSHOP_YOO_KASSA_TEST_WARNING', 'Вы включили тестовый режим приема платежей. Проверьте, как проходит оплата, и напишите менеджеру ЮKassa. Он выдаст рабочие shopId и Секретный ключ. Инструкция'); +define('_JSHOP_YOO_KASSA_TEST_WARNING', 'Вы включили тестовый режим приёма платежей. Проверьте, как проходит оплата, и напишите менеджеру ЮKassa. Он выдаст рабочие shopId и Секретный ключ. Инструкция'); define('_JSHOP_YOO_METHOD_YOO_MONEY_DESCRIPTION', 'ЮMoney'); define('_JSHOP_YOO_METHOD_CARDS_DESCRIPTION', 'Банковские карты'); define('_JSHOP_YOO_METHOD_BANK_CARD_DESCRIPTION', 'Банковские карты'); define('_JSHOP_YOO_METHOD_CASH_DESCRIPTION', 'Наличные через терминалы'); define('_JSHOP_YOO_METHOD_MOBILE_BALANCE_DESCRIPTION', 'Баланс мобильного'); -define('_JSHOP_YOO_METHOD_WEBMONEY_DESCRIPTION', 'Кошелек WebMoney'); +define('_JSHOP_YOO_METHOD_WEBMONEY_DESCRIPTION', 'Кошелёк WebMoney'); define('_JSHOP_YOO_METHOD_ALFABANK_DESCRIPTION', 'Альфа-Клик'); define('_JSHOP_YOO_METHOD_SBERBANK_DESCRIPTION', 'SberPay'); define('_JSHOP_YOO_METHOD_TINKOFF_BANK_DESCRIPTION', 'Интернет-банк Тинькофф'); @@ -87,7 +87,7 @@ define('_JSHOP_YOO_METHOD_BANK_CARD_DESCRIPTION_PUBLIC', 'Банковские карты'); define('_JSHOP_YOO_METHOD_CASH_DESCRIPTION_PUBLIC', 'Наличные через терминалы'); define('_JSHOP_YOO_METHOD_MOBILE_BALANCE_DESCRIPTION_PUBLIC', 'Баланс мобильного'); -define('_JSHOP_YOO_METHOD_WEBMONEY_DESCRIPTION_PUBLIC', 'Кошелек WebMoney'); +define('_JSHOP_YOO_METHOD_WEBMONEY_DESCRIPTION_PUBLIC', 'Кошелёк WebMoney'); define('_JSHOP_YOO_METHOD_ALFABANK_DESCRIPTION_PUBLIC', 'Альфа-Клик'); define('_JSHOP_YOO_METHOD_SBERBANK_DESCRIPTION_PUBLIC', 'SberPay'); define('_JSHOP_YOO_METHOD_TINKOFF_BANK_DESCRIPTION_PUBLIC', 'Интернет-банк Тинькофф'); @@ -103,21 +103,21 @@ define('_JSHOP_YOO_KASSA_MPOS_LABEL', 'Страница успеха для способа «Оплата картой при доставке»'); define('_JSHOP_YOO_KASSA_MPOS_HELP', 'Это страница с информацией о доставке. Укажите на ней, когда привезут товар и как его можно будет оплатить'); -define('_JSHOP_YOO_MONEY_HEAD', 'Для работы с модулем нужно открыть кошелек на ЮMoney и +define('_JSHOP_YOO_MONEY_HEAD', 'Для работы с модулем нужно открыть кошелёк на ЮMoney и зарегистрировать приложение на сайте ЮMoney'); -define('_JSHOP_YOO_MONEY_ON', 'Включить прием платежей в кошелек на ЮMoney'); +define('_JSHOP_YOO_MONEY_ON', 'Включить приём платежей в кошелёк на ЮMoney'); define('_JSHOP_YOO_MONEY_REDIRECT_HELP', 'Скопируйте эту ссылку в поле Redirect URL на странице регистрации приложения.'); -define('_JSHOP_YOO_MONEY_SET_HEAD', 'Настройки приема платежей'); +define('_JSHOP_YOO_MONEY_SET_HEAD', 'Настройки приёма платежей'); define('_JSHOP_YOO_MONEY_WALLET', 'Номер кошелька'); define('_JSHOP_YOO_MONEY_PSW', 'Секретное слово'); define('_JSHOP_YOO_MONEY_SELECT_HEAD', 'Настройка сценария оплаты'); define('_JSHOP_YOO_MONEY_SELECT_LABEL', 'Способы оплаты'); -define('_JSHOP_YOO_METHOD_YM2_DESCRIPTION', 'Кошелек ЮMoney'); +define('_JSHOP_YOO_METHOD_YM2_DESCRIPTION', 'Кошелёк ЮMoney'); define('_JSHOP_YOO_METHOD_CARDS2_DESCRIPTION', 'Банковская карта'); -define('_JSHOP_YOO_METHOD_YM2_DESCRIPTION_PUBLIC', 'Кошелек ЮMoney'); +define('_JSHOP_YOO_METHOD_YM2_DESCRIPTION_PUBLIC', 'Кошелёк ЮMoney'); define('_JSHOP_YOO_METHOD_CARDS2_DESCRIPTION_PUBLIC', 'Банковская карта'); define('_JSHOP_YOO_COMMON_HEAD', 'Дополнительные настройки для администратора'); @@ -136,13 +136,13 @@ define('_JSHOP_YOO_LICENSE','Лицензионный договор:'); define('_JSHOP_YOO_LICENSE_TEXT2',"

Любое использование Вами программы означает полное и безоговорочное принятие Вами условий лицензионного договора, размещенного по адресу https://yoomoney.ru/doc.xml?id=527132 (далее – «Лицензионный договор»). Если Вы не принимаете условия Лицензионного договора в полном объёме, Вы не имеете права использовать программу в каких-либо целях.

"); define('_JSHOP_YOO_TESTMODE_DESCRIPTION', 'Использовать в тестовом режиме?'); -define('_JSHOP_YOO_MODE_DESCRIPTION', 'Способ приема платежей:'); +define('_JSHOP_YOO_MODE_DESCRIPTION', 'Способ приёма платежей:'); define('_JSHOP_YOO_MODE1_DESCRIPTION', 'ЮMoneyи'); define('_JSHOP_YOO_MODE2_DESCRIPTION', 'ЮKassa (выбор оплаты на стороне сайта)'); define('_JSHOP_YOO_MODE3_DESCRIPTION', 'ЮKassa (выбор оплаты на стороне ЮKassa)'); -define('_JSHOP_YOO_REG_IND', 'Если у вас нет аккаунта в ЮMoney, то следует зарегистрироваться тут - https://yoomoney.ru/
ВАЖНО! Вам нужно будет указать ссылку для приема HTTP уведомлений здесь - https://sp-yoomoney.ru/myservices/online.xml'); +define('_JSHOP_YOO_REG_IND', 'Если у вас нет аккаунта в ЮMoney, то следует зарегистрироваться тут - https://yoomoney.ru/
ВАЖНО! Вам нужно будет указать ссылку для приёма HTTP уведомлений здесь - https://sp-yoomoney.ru/myservices/online.xml'); -define('_JSHOP_YOO_REG_ORG', 'Для работы с модулем необходимо подключить магазин к YooMoney for business. После подключения вы получите параметры для приема платежей (идентификатор магазина — shopId и номер витрины — scid).'); +define('_JSHOP_YOO_REG_ORG', 'Для работы с модулем необходимо подключить магазин к YooMoney for business. После подключения вы получите параметры для приёма платежей (идентификатор магазина — shopId и номер витрины — scid).'); define('_JSHOP_YOO_METHODS_DESCRIPTION', 'Укажите необходимые способы оплаты'); define('_JSHOP_YOO_PASSWORD', 'Секретное слово (shopPassword) для обмена сообщениями:'); define('_JSHOP_YOO_SHOPID', 'Идентификатор вашего магазина в ЮMoney (ShopID):'); @@ -242,4 +242,5 @@ define('_JSHOP_YOO_BTN_BACK', 'Назад'); define('_JSHOP_REDIRECT_TO_PAYMENT_PAGE', 'Перенаправление на страницу оплаты'); -define('_JSHOP_PAYMENT_NUMBER', 'Номер заказа %s'); \ No newline at end of file +define('_JSHOP_PAYMENT_NUMBER', 'Номер заказа %s'); +define('_JSHOP_YOO_KASSA_REFUND_SUCCEDED_ORDER_HISTORY', 'Выполнен возврат. Сумма %s руб.'); \ No newline at end of file diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/JVersionDependenciesHelper.php b/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/JVersionDependenciesHelper.php new file mode 100644 index 0000000..c721a2f --- /dev/null +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/JVersionDependenciesHelper.php @@ -0,0 +1,110 @@ +joomlaVersion = (version_compare(JVERSION, '3.0', '<') == 1) ? 2 : 3; + $this->joomlaVersion = (version_compare(JVERSION, '4.0', '<') == 1) ? $this->joomlaVersion : 4; + } + + /** + * Возвращает версию Joomla! от 2 до 4 версии + * + * @return int + */ + public function getJoomlaVersion() + { + return $this->joomlaVersion; + } + + /** + * Функция-обертка для getJsDate() + * + * @return mixed + */ + public function getJsDate() + { + if ($this->joomlaVersion == 4) { + return \JSHelper::getJsDate(); + } + + return getJsDate(); + } + + /** + * Функция-обертка для получения инстанса \Joomla\Component\Jshopping\Site\Table\AddonTable + * + * @return mixed + */ + public function getAddonTableObj() + { + if (JVERSION == 4) { + $app = \JFactory::getApplication(); + + /** @var MVCFactoryInterface $factory */ + $factory = $app->bootComponent('com_jshopping')->getMVCFactory(); + return $factory->createTable('addon', 'Site'); + } + return \JTable::getInstance('addon', 'jshop'); + } + + /** + * Возвращает составную часть имени файлов и директорий в зависимости от версии Joomla! + * Необходимо для получения шаблонов формы для модуля в админ-панели для конкретной версии Joomla! + * + * @return string + */ + public function getFilesVersionPostfix() + { + switch ($this->joomlaVersion) { + case 2: + return '2x'; + case 3: + return '3x'; + default: + return ''; + } + } + + /** + * Функция-обертка для регистрации обработчика события + * + * @param string $eventName - название события + * @param array $listenerData - обработчик + */ + public function registerEventListener($eventName, $listenerData) + { + switch ($this->joomlaVersion) { + case 2: + case 3: + $dispatcher = \JDispatcher::getInstance(); + $dispatcher->register($eventName, $listenerData); + break; + default: + \JFactory::getApplication()->getDispatcher()->addListener($eventName, $listenerData); + } + } + + /** + * Функция-обертка для getSefLink() + * + * @param string $link + * @return mixed + */ + public function getSefLink($link) + { + if ($this->joomlaVersion == 4) { + return \JSHelper::SEFLink($link); + } + + return SEFLink($link); + } +} \ No newline at end of file diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/Logger.php b/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/Logger.php new file mode 100644 index 0000000..634c8c7 --- /dev/null +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/Logger.php @@ -0,0 +1,52 @@ + $value) { + if (is_scalar($value)) { + $replace['{'.$key.'}'] = $value; + } else { + $replace['{'.$key.'}'] = json_encode($value); + } + } + + if (!empty($replace)) { + $message = strtr($message, $replace); + } + + $fileName = $this->getLogFileName(); + $fd = @fopen($fileName, 'a'); + + if ($fd) { + flock($fd, LOCK_EX); + fwrite($fd, date(DATE_ATOM).' ['.$level.'] '.$message."\r\n"); + flock($fd, LOCK_UN); + fclose($fd); + } + } + + /** + * Возвращает путь к файлу лога + * + * @return string + */ + public function getLogFileName() + { + return realpath(JSH_DIR).'/log/pm_yoomoney.log'; + } +} \ No newline at end of file diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/OrderHelper.php b/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/OrderHelper.php new file mode 100644 index 0000000..7cf2427 --- /dev/null +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/OrderHelper.php @@ -0,0 +1,38 @@ +dependenciesHelper = new JVersionDependenciesHelper(); + } + + /** + * Сохраняет запись в истории к заказу + * + * @param $order + * @param $comments + * @return mixed + */ + public function saveOrderHistory($order, $comments) + { + $history = \JSFactory::getTable('orderHistory', 'jshop'); + $history->order_id = $order->order_id; + $history->order_status_id = $order->order_status; + $history->status_date_added = $this->dependenciesHelper->getJsDate(); + $history->customer_notify = 0; + $history->comments = $comments; + + return $history->store(); + } +} \ No newline at end of file diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/ReceiptHelper.php b/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/ReceiptHelper.php new file mode 100644 index 0000000..fb13da5 --- /dev/null +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/ReceiptHelper.php @@ -0,0 +1,102 @@ +logger = new Logger(); + $this->orderHelper = new OrderHelper(); + } + + /** + * Выполняет отправку второго чека + * + * @param $orderId + * @param $kassa + * @param $status + * @return void|KassaSecondReceiptModel + */ + public function sendSecondReceipt($orderId, $kassa, $status) + { + + if (!$this->isNeedSecondReceipt( + $status, + $kassa->isSendReceipt(), + $kassa->isSendSecondReceipt(), + $kassa->getSecondReceiptStatus()) + ) { + return; + } + + $order = \JSFactory::getTable('order', 'jshop'); + $order->load($orderId); + + $apiClient = $kassa->getClient(); + + $orderInfo = array( + 'orderId' => $order->order_id, + 'user_email' => $order->email, + 'user_phone' => $order->phone, + ); + + $orderModel = new OrderModel(); + + try { + $paymentInfo = $apiClient->getPaymentInfo($orderModel->getPaymentIdByOrderId($order->order_id)); + } catch (\Exception $e) { + $this->logger->log('info', 'fail get payment info'); + return; + } + + $secondReceipt = new KassaSecondReceiptModel($paymentInfo, $orderInfo, $apiClient); + if ($secondReceipt->sendSecondReceipt()) { + $this->orderHelper->saveOrderHistory( + $order, + sprintf( + _JSHOP_YOO_KASSA_SEND_SECOND_RECEIPT_HISTORY, + number_format($secondReceipt->getSettlementsSum(), 2, '.', ' ') + ) + ); + } + } + + /** + * Проверка условий необходимости отправки второго чека + * + * @param $status + * @param $isSendReceipt + * @param $isSendSecondReceipt + * @param $secondReceiptStatus + * + * @return bool + */ + private function isNeedSecondReceipt($status, $isSendReceipt, $isSendSecondReceipt, $secondReceiptStatus) + { + if (!$isSendReceipt) { + return false; + } elseif (!$isSendSecondReceipt) { + return false; + } elseif ((int)$status !== (int)$secondReceiptStatus) { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/TransactionHelper.php b/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/TransactionHelper.php new file mode 100644 index 0000000..cb74c02 --- /dev/null +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/TransactionHelper.php @@ -0,0 +1,357 @@ +logger = new Logger(); + $this->yooNotificationHelper = new YoomoneyNotificationFactory(); + $this->orderHelper = new OrderHelper(); + $this->receiptHelper = new ReceiptHelper(); + $this->orderModel = new OrderModel(); + } + + /** + * Обрабатывает уведомление от Юkassa в зависимости от статуса платежа в уведомлении + * + * @param KassaPaymentMethod $kassa + * @param array $pmConfigs + * @param OrderTable $order + * @return bool|void + * @throws \Exception + */ + public function processNotification($kassa, $pmConfigs, $order) + { + $notificationObj = $this->yooNotificationHelper->getNotificationObject(); + $paymentId = $notificationObj->getObject()->getId(); + + $refund = null; + if ($notificationObj->getEvent() === NotificationEventType::REFUND_SUCCEEDED) { + $refundId = $notificationObj->getObject()->getId(); + $refund = $kassa->fetchRefund($refundId); + + if (!$refund) { + $this->logger->log('debug', 'Notification error: refund is not exist'); + header('HTTP/1.1 404 Refund is not exist'); + die(); + } + $paymentId = $refund->getPaymentId(); + } + + $payment = $kassa->fetchPayment($paymentId); + if (!$payment) { + $this->logger->log('debug', 'Notification error: payment is not exist'); + header('HTTP/1.1 404 Payment not exists'); + die(); + } + + if ( + $notificationObj->getEvent() === NotificationEventType::PAYMENT_SUCCEEDED + && $payment->getStatus() === PaymentStatus::SUCCEEDED + ) { + $this->processSucceedNotification($pmConfigs, $order, $payment, $kassa); + return true; + } + + if ( + $notificationObj->getEvent() === NotificationEventType::PAYMENT_WAITING_FOR_CAPTURE + && $payment->getStatus() === PaymentStatus::WAITING_FOR_CAPTURE + ) { + $this->processWaitingForCaptureNtfctn($pmConfigs, $order, $payment, $kassa, $notificationObj); + return true; + } + + if ( + $notificationObj->getEvent() === NotificationEventType::PAYMENT_CANCELED + && $payment->getStatus() === PaymentStatus::CANCELED + && $kassa->isEnableHoldMode() + ) { + $this->processCanceledHoldPaymentNtfctn($pmConfigs, $order, $payment); + return true; + } + + if ( + $notificationObj->getEvent() === NotificationEventType::PAYMENT_CANCELED + && $payment->getStatus() === PaymentStatus::CANCELED + && !$kassa->isEnableHoldMode() + ) { + $this->logger->log('info', 'Canceled payment ' . $payment->getId()); + $this->processCanceledPaymentNtfctn($order); + return true; + } + + if ( + $notificationObj->getEvent() === NotificationEventType::REFUND_SUCCEEDED + && $payment->getStatus() === PaymentStatus::SUCCEEDED + && $refund->getStatus() == RefundStatus::SUCCEEDED + && !$this->isRefundAlreadyGot($refund->getId()) + ) { + $this->logger->log( + 'info', 'Refund payment ' . $payment->getId() . '. Refund ID ' . $refund->getId() + ); + $this->processRefundNtfctn($order, $refund); + return true; + } + + if ( + $notificationObj->getEvent() === NotificationEventType::DEAL_CLOSED + || $notificationObj->getEvent() === NotificationEventType::PAYOUT_CANCELED + || $notificationObj->getEvent() === NotificationEventType::PAYOUT_SUCCEEDED + ) { + return true; + } + + return false; + } + + /** + * Проверяет в БД было ли уже получено уведомление о возврате с таким id + * + * @param $refundId + * @return bool + */ + private function isRefundAlreadyGot($refundId) + { + try { + $refund = $this->orderModel->getRefundById($refundId); + } catch (\Exception $e) { + $this->logger->log('debug', 'Failed to read a refund from DB: ' . $e->getMessage()); + } + + return !empty($refund); + } + + /** + * Выполняет действия, если получено уведомление о статусе payment.succeeded + * + * @param $pmConfigs + * @param $order + * @param $payment + * @param KassaPaymentMethod $kassa + */ + private function processSucceedNotification($pmConfigs, $order, $payment, $kassa) + { + $jshopConfig = \JSFactory::getConfig(); + + /** @var jshopCheckout $checkout */ + $checkout = \JSFactory::getModel('checkout', 'jshop'); + $endStatus = $pmConfigs['transaction_end_status']; + $order->order_created = 1; + $order->order_status = $endStatus; + $order->store(); + + try { + if ($jshopConfig->send_order_email) { + $checkout->sendOrderEmail($order->order_id); + } + } catch (\Exception $exception) { + $this->logger->log('debug', $exception->getMessage()); + } + + $product_stock_removed = true; + if ($jshopConfig->order_stock_removed_only_paid_status) { + $product_stock_removed = in_array($endStatus, $jshopConfig->payment_status_enable_download_sale_file); + } + + if ($product_stock_removed) { + $order->changeProductQTYinStock("-"); + } + + $this->receiptHelper->sendSecondReceipt($order->order_id, $kassa, $endStatus); + + $checkout->changeStatusOrder($order->order_id, $endStatus, 0); + + $paymentMethod = $payment->getPaymentMethod(); + if($paymentMethod->getType() == PaymentMethodType::B2B_SBERBANK) { + $message = $this->getSuccessOrderHistoryMessageForB2B($paymentMethod); + } + + if (!empty($message)) { + $this->orderHelper->saveOrderHistory($order, $message); + } + } + + /** + * Возвращает сообщение для истории статусов заказа, если тип платежа b2b_sberbank + * + * @param $paymentMethod + * @return string + */ + private function getSuccessOrderHistoryMessageForB2B($paymentMethod) + { + $payerBankDetails = $paymentMethod->getPayerBankDetails(); + + $fields = array( + 'fullName' => 'Полное наименование организации', + 'shortName' => 'Сокращенное наименование организации', + 'adress' => 'Адрес организации', + 'inn' => 'ИНН организации', + 'kpp' => 'КПП организации', + 'bankName' => 'Наименование банка организации', + 'bankBranch' => 'Отделение банка организации', + 'bankBik' => 'БИК банка организации', + 'account' => 'Номер счета организации', + ); + $message = ''; + foreach ($fields as $field => $caption) { + if (isset($requestData[$field])) { + $message .= $caption.': '.$payerBankDetails->offsetGet($field).'\n'; + } + } + return $message; + } + + /** + * Выполняет действия, если получено уведомление о статусе payment.waiting_for_capture + * + * @param $pmConfigs + * @param $order + * @param $payment + * @param $kassa + * @param $notificationObj + */ + private function processWaitingForCaptureNtfctn($pmConfigs, $order, $payment, $kassa, $notificationObj) + { + if ($kassa->isEnableHoldMode()) { + $this->logger->log('info', 'Hold payment '.$payment->getId()); + + /** @var jshopCheckout $checkout */ + $checkout = \JSFactory::getModel('checkout', 'jshop'); + $onHoldStatus = $pmConfigs['yookassa_hold_mode_on_hold_status']; + $order->order_created = 1; + $order->order_status = $onHoldStatus; + $order->store(); + $checkout->changeStatusOrder($order->order_id, $onHoldStatus, 0); + $this->orderHelper->saveOrderHistory( + $order, + sprintf(_JSHOP_YOO_HOLD_MODE_COMMENT_ON_HOLD, + $payment->getExpiresAt()->format('d.m.Y H:i')) + ); + + } else { + $payment = $kassa->capturePayment($notificationObj->getObject()); + if (!$payment || $payment->getStatus() !== PaymentStatus::SUCCEEDED) { + $this->logger->log('debug', 'Capture payment error'); + header('HTTP/1.1 400 Bad Request'); + } + } + } + + /** + * Выполняет действия, если получено уведомление о статусе payment.canceled и включен режим холдирования + * + * @param $pmConfigs + * @param $order + * @param $payment + */ + private function processCanceledHoldPaymentNtfctn($pmConfigs, $order, $payment) + { + $this->logger->log('info', 'Canceled hold payment ' . $payment->getId()); + + /** @var jshopCheckout $checkout */ + $checkout = \JSFactory::getModel('checkout', 'jshop'); + $cancelHoldStatus = $pmConfigs['yookassa_hold_mode_cancel_status']; + $order->order_created = 1; + $order->order_status = $cancelHoldStatus; + $order->store(); + $checkout->changeStatusOrder($order->order_id, $cancelHoldStatus, 0); + } + + /** + * Выполняет действия, если получено уведомление о статусе payment.canceled + * + * @param $order + */ + private function processCanceledPaymentNtfctn($order) + { + /** @var jshopCheckout $checkout */ + $checkout = \JSFactory::getModel('checkout', 'jshop'); + + $order->order_created = 1; + $order->order_status = self::CANCELED_STATUS_ID; + $order->store(); + $checkout->changeStatusOrder($order->order_id, self::CANCELED_STATUS_ID, 0); + } + + /** + * Выполняет действия, если получено уведомление о статусе refund.succeeded + * + * @param $order + * @param Refund $refund + */ + private function processRefundNtfctn($order, $refund) + { + try { + $this->orderModel->insertRefund($order->getId(), $refund); + } catch (\Exception $e) { + $this->logger->log('debug', 'Failed to save a refund to DB: ' . $e->getMessage()); + + $this->logger->log('debug', 'Will create the refund table.'); + try { + $tableChecker = new \YooMoney\Updater\Tables\TableChecker(); + $tableChecker->checkYoomoneyRefunds(); + } catch (\Exception $e) { + $this->logger->log('debug', 'Failure to create the refund table: ' . $e->getMessage()); + + return; + } + + $this->orderModel->insertRefund($order->getId(), $refund); + } + + /** @var jshopCheckout $checkout */ + $checkout = \JSFactory::getModel('checkout', 'jshop'); + + $order->order_created = 1; + $order->order_status = self::REFUNDED_STATUS_ID; + $order->store(); + $checkout->changeStatusOrder($order->order_id, self::REFUNDED_STATUS_ID, 0); + $this->orderHelper->saveOrderHistory( + $order, + sprintf(_JSHOP_YOO_KASSA_REFUND_SUCCEDED_ORDER_HISTORY, + $refund->getAmount()->getValue()) + ); + } +} \ No newline at end of file diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/YoomoneyNotificationFactory.php b/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/YoomoneyNotificationFactory.php new file mode 100644 index 0000000..b5ba00b --- /dev/null +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/Helpers/YoomoneyNotificationFactory.php @@ -0,0 +1,73 @@ +logger = new Logger(); + } + + /** + * Возвращает объект уведомления от Юkassa, если он был получен ранее, или сначала + * преобразует уведомление от Юkassa в объект + * + * @return AbstractNotification + * @throws \Exception + */ + public function getNotificationObject() + { + if ($this->notificationObject) { + return $this->notificationObject; + } + + $source = file_get_contents('php://input'); + $this->logger->log('debug', 'Notification body source: '.$source); + if (empty($source)) { + $this->logger->log('debug', 'Notification error: body is empty!'); + header('HTTP/1.1 400 Body is empty'); + die(); + } + + $data = json_decode($source, true); + if (empty($data)) { + $this->logger->log('debug', 'Notification error: invalid body!'); + header('HTTP/1.1 400 Invalid body'); + die(); + } + + $factory = new NotificationFactory(); + $notification = $factory->factory($data); + $this->setNotificationObj($notification); + + return $notification; + } + + /** + * Сеттер для объекта уведомления + * + * @param $object + */ + private function setNotificationObj($object) + { + $this->notificationObject = $object; + } +} \ No newline at end of file diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/Model/KassaPaymentMethod.php b/src/components/com_jshopping/payments/pm_yoomoney/lib/Model/KassaPaymentMethod.php index c60fa8c..63fedba 100644 --- a/src/components/com_jshopping/payments/pm_yoomoney/lib/Model/KassaPaymentMethod.php +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/Model/KassaPaymentMethod.php @@ -16,12 +16,12 @@ use YooKassa\Request\Payments\CreatePaymentRequest; use YooKassa\Request\Payments\Payment\CreateCaptureRequest; use YooKassa\Request\Payments\Payment\CreateCaptureRequestBuilder; -use YooMoney\Model\SbbolException; +use YooKassa\Request\Refunds\RefundResponse; require_once JPATH_ROOT.'/components/com_jshopping/payments/pm_yoomoney_sbbol/SbbolException.php'; if (!defined(_JSHOP_YOO_VERSION)) { - define('_JSHOP_YOO_VERSION', '2.3.0'); + define('_JSHOP_YOO_VERSION', '2.3.1'); } @@ -44,7 +44,7 @@ class KassaPaymentMethod /** * KassaPaymentMethod constructor. * - * @param \pm_yoomoney $module + * @param \pm_yoomoney|\pm_yoomoney_sbbol $module * @param array $pmConfig */ public function __construct($module, $pmConfig) @@ -301,6 +301,25 @@ public function fetchPayment($paymentId) return $payment; } + /** + * Получает по API Юkassa объект возврата по переданному id + * + * @param string $refundId + * + * @return RefundResponse|null + */ + public function fetchRefund($refundId) + { + $refund = null; + try { + $refund = $this->getClient()->getRefundInfo($refundId); + } catch (\Exception $e) { + $this->module->log('error', 'Failed to fetch refund information from API: '.$e->getMessage()); + } + + return $refund; + } + /** * @param \YooKassa\Request\Payments\CreatePaymentRequestBuilder|CreateCaptureRequestBuilder $builder * @param array $products diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/Model/KassaSecondReceiptModel.php b/src/components/com_jshopping/payments/pm_yoomoney/lib/Model/KassaSecondReceiptModel.php index 0d4ce5d..6aa78c3 100644 --- a/src/components/com_jshopping/payments/pm_yoomoney/lib/Model/KassaSecondReceiptModel.php +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/Model/KassaSecondReceiptModel.php @@ -129,7 +129,7 @@ private function buildSecondReceipt($lastReceipt, $paymentInfo, $orderInfo) $resendItems = $this->getResendItems($lastReceipt->getItems()); - if (count($resendItems['items']) < 1) { + if (!count($resendItems['items'])) { $this->log("info", "Second receipt isn't need"); return; } diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/Model/OrderModel.php b/src/components/com_jshopping/payments/pm_yoomoney/lib/Model/OrderModel.php index 30ffcaf..22e399d 100644 --- a/src/components/com_jshopping/payments/pm_yoomoney/lib/Model/OrderModel.php +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/Model/OrderModel.php @@ -3,6 +3,7 @@ namespace YooMoney\Model; use YooKassa\Model\PaymentInterface; +use YooKassa\Model\RefundInterface; class OrderModel { @@ -31,6 +32,12 @@ public function savePayment($orderId, $payment) } } + /** + * Возвращает payment id по id заказа + * + * @param $orderId + * @return null|string + */ public function getPaymentIdByOrderId($orderId) { $query = $this->_db->getQuery(true); @@ -45,6 +52,28 @@ public function getPaymentIdByOrderId($orderId) return $record[0]; } + /** + * Возвращает id заказа по payment id + * + * @param $paymentId + * @return null|string + */ + public function getOrderIdByPaymentId($paymentId) + { + $query = $this->_db->getQuery(true); + $query->select('order_id') + ->from('#__yoomoney_payments') + ->where( + $this->_db->quoteName('payment_id') . ' = \'' . $paymentId . '\'' + ); + $this->_db->setQuery($query); + $record = $this->_db->loadRow(); + if (empty($record)) { + return null; + } + return $record[0]; + } + /** * @param int $orderId * @param PaymentInterface $payment @@ -116,4 +145,52 @@ private function updatePayment($orderId, $payment) \JError::raiseError(500, $e->getMessage()); } } + + /** + * Возвращает запись из таблицы возвратов + * + * @param $refundId + * @return null|array + */ + public function getRefundById($refundId) + { + $query = $this->_db->getQuery(true); + $query->select('*') + ->from('#__yoomoney_refunds') + ->where( + $this->_db->quoteName('refund_id') . ' = \'' . $refundId . '\'' + ); + $this->_db->setQuery($query); + $record = $this->_db->loadRow(); + if (empty($record)) { + return null; + } + return $record; + } + + /** + * Добавляет запись в таблицу возвратов + * + * @param int $orderId + * @param RefundInterface $refund + */ + public function insertRefund($orderId, $refund) + { + $query = $this->_db->getQuery(true); + $query->clear()->insert('#__yoomoney_refunds') + ->columns( + array( + $this->_db->quoteName('refund_id'), + $this->_db->quoteName('order_id'), + $this->_db->quoteName('created_at') + ) + ) + ->values( + $this->_db->quote($refund->getId()) . "," + . (int)$orderId . ',' + . $this->_db->quote($refund->getCreatedAt()->format('Y-m-d H:i:s')) + ); + $this->_db->setQuery($query); + $this->_db->execute(); + } } \ No newline at end of file diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/Updater/Tables/TableChecker.php b/src/components/com_jshopping/payments/pm_yoomoney/lib/Updater/Tables/TableChecker.php new file mode 100644 index 0000000..cc2ad1f --- /dev/null +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/Updater/Tables/TableChecker.php @@ -0,0 +1,24 @@ +setQuery($query)->execute(); + } +} \ No newline at end of file diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/composer/installed.json b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/composer/installed.json index f9bff43..8229165 100644 --- a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/composer/installed.json +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/composer/installed.json @@ -55,17 +55,17 @@ }, { "name": "yoomoney/yookassa-sdk-php", - "version": "2.2.4", - "version_normalized": "2.2.4.0", + "version": "2.2.5", + "version_normalized": "2.2.5.0", "source": { "type": "git", "url": "https://github.com/yoomoney/yookassa-sdk-php.git", - "reference": "fbbc5d10fd1dd71e976740e55b69dd976884f7e0" + "reference": "8415cc1db919dd157a6f28a7300e96a1b74bd7f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yoomoney/yookassa-sdk-php/zipball/fbbc5d10fd1dd71e976740e55b69dd976884f7e0", - "reference": "fbbc5d10fd1dd71e976740e55b69dd976884f7e0", + "url": "https://api.github.com/repos/yoomoney/yookassa-sdk-php/zipball/8415cc1db919dd157a6f28a7300e96a1b74bd7f0", + "reference": "8415cc1db919dd157a6f28a7300e96a1b74bd7f0", "shasum": "" }, "require": { @@ -80,7 +80,7 @@ "mockery/mockery": "^0.9.9", "phpunit/phpunit": "^4.8.35 || ^5.7" }, - "time": "2021-12-09T12:17:35+00:00", + "time": "2021-12-28T12:16:53+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -109,7 +109,7 @@ ], "support": { "issues": "https://github.com/yoomoney/yookassa-sdk-php/issues", - "source": "https://github.com/yoomoney/yookassa-sdk-php/tree/2.2.4" + "source": "https://github.com/yoomoney/yookassa-sdk-php/tree/2.2.5" }, "install-path": "../yoomoney/yookassa-sdk-php" } diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/composer/installed.php b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/composer/installed.php index 4c4c58c..e08bba9 100644 --- a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/composer/installed.php +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/composer/installed.php @@ -5,7 +5,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => 'd1a3b7a8f79f88baa6935584fb2d75d0c305893b', + 'reference' => '34530a05da1cb743979b233d065812769e6a2b4d', 'name' => '__root__', 'dev' => true, ), @@ -16,7 +16,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => 'd1a3b7a8f79f88baa6935584fb2d75d0c305893b', + 'reference' => '34530a05da1cb743979b233d065812769e6a2b4d', 'dev_requirement' => false, ), 'psr/log' => array( @@ -29,12 +29,12 @@ 'dev_requirement' => false, ), 'yoomoney/yookassa-sdk-php' => array( - 'pretty_version' => '2.2.4', - 'version' => '2.2.4.0', + 'pretty_version' => '2.2.5', + 'version' => '2.2.5.0', 'type' => 'library', 'install_path' => __DIR__ . '/../yoomoney/yookassa-sdk-php', 'aliases' => array(), - 'reference' => 'fbbc5d10fd1dd71e976740e55b69dd976884f7e0', + 'reference' => '8415cc1db919dd157a6f28a7300e96a1b74bd7f0', 'dev_requirement' => false, ), ), diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/CHANGELOG.md b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/CHANGELOG.md index 53f4746..5ad1bbb 100644 --- a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/CHANGELOG.md +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/CHANGELOG.md @@ -1,3 +1,8 @@ +### v2.2.5 от 28.12.2021 +* Добавлен метод проверки IP адреса уведомления от Юkassa среди известных адресов Юkassa +* Добавлены тесты +* Добавлен пример в документацию + ### v2.2.4 от 09.12.2021 * Фикс метода jsonSerialize() для ReceiptResponseItem * Добавлена проверка на параметр platform_fee_amount при создании запроса для двухстадийного платежа diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/composer.json b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/composer.json index a660860..1de06f4 100644 --- a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/composer.json +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/composer.json @@ -11,7 +11,7 @@ "email": "cms@yoomoney.ru" } ], - "version": "2.2.4", + "version": "2.2.5", "require": { "php": ">=5.3.0", "ext-curl": "*", diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/lib/Client.php b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/lib/Client.php index 77d5d0f..24622be 100644 --- a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/lib/Client.php +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/lib/Client.php @@ -112,7 +112,7 @@ class Client extends BaseClient /** * Текущая версия библиотеки */ - const SDK_VERSION = '2.2.4'; + const SDK_VERSION = '2.2.5'; /** * Получить список платежей магазина diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/lib/Client/BaseClient.php b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/lib/Client/BaseClient.php index 7467ad4..430c1ed 100644 --- a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/lib/Client/BaseClient.php +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/lib/Client/BaseClient.php @@ -43,6 +43,7 @@ use YooKassa\Common\ResponseObject; use YooKassa\Helpers\Config\ConfigurationLoader; use YooKassa\Helpers\Config\ConfigurationLoaderInterface; +use YooKassa\Helpers\SecurityHelper; class BaseClient { @@ -291,6 +292,22 @@ public function setMaxRequestAttempts($attempts) return $this; } + + /** + * Метод проверяет, находится ли IP адрес среди IP адресов Юkassa, с которых отправляются уведомления + * + * @param string $ip - IPv4 или IPv6 адрес webhook уведомления + * @return bool + * + * @throws Exception - исключение будет выброшено, если будет передан IP адрес неверного формата + */ + public function isNotificationIPTrusted($ip) + { + $securityHelper = new SecurityHelper(); + + return $securityHelper->isIPTrusted($ip); + } + /** * Кодирует массив данных в JSON строку * diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/lib/Helpers/SecurityHelper.php b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/lib/Helpers/SecurityHelper.php new file mode 100644 index 0000000..fdc5f0a --- /dev/null +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/lib/Helpers/SecurityHelper.php @@ -0,0 +1,195 @@ +isIPv6($ip)) { + return $this->checkInIPv4TrustedList($ip); + } + + if (!$this->isIPv4($ip)) { + return $this->checkInIPv6TrustedList($ip); + } + throw new \Exception( + 'Could not recognize IPv4 or IPv6: ' . $ip + ); + } + + /** + * Проверяет, является ли переданное в функцию значение IPv6 адресом + * + * @param $ip - IP адрес + * @return bool - true - является, false - не является + */ + private function isIPv6($ip) + { + return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) != false; + } + + /** + * Проверяет, является ли переданное в функцию значение IPv4 адресом + * + * @param $ip - IP адрес + * @return bool - true - является, false - не является + */ + private function isIPv4($ip) + { + return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) != false; + } + + /** + * Проверяет IPv4 адрес в списке IPv4 адресов Юkassa + * + * @param $ip - IPv4 адрес + * @return bool + */ + private function checkInIPv4TrustedList($ip) + { + foreach($this->getIPv4TrustedList() as $range) { + if ($this->isIPInV4Range($ip, $range)) { + return true; + } + } + return false; + } + + /** + * Проверяет IPv6 адрес в списке IPv6 адресов Юkassa + * + * @param $ip - IPv6 адрес + * @return bool + */ + private function checkInIPv6TrustedList($ip) + { + foreach($this->getIPv6TrustedList() as $range) { + if ($this->isIPInV6Range($ip, $range)) { + return true; + } + } + return false; + } + + /** + * Осуществляет проверку, входит ли IPv4 адрес $ip в диапазон IPv4 адресов $range + * + * @param $ip - IPv4 адрес + * @param $range - IPv4 адрес, или диапазон IPv4 адресов в формате CIDR + * @return bool + */ + private function isIPInV4Range($ip, $range) + { + $ip_dec = ip2long($ip); + + if (strpos($range, '/') === false) { + return ip2long($ip) == ip2long($range); + } + list($range, $netmask) = explode('/', $range, 2); + list($a,$b,$c,$d) = explode('.', $range); + + $range = sprintf("%u.%u.%u.%u", $a, $b, $c, $d); + $range_dec = ip2long($range); + + $wildcard_dec = pow(2, (32-$netmask)) - 1; + $netmask_dec = ~ $wildcard_dec; + + return (($ip_dec & $netmask_dec) == ($range_dec & $netmask_dec)); + } + + /** + * Осуществляет проверку, входит ли IPv6 адрес $ip в диапазон IPv6 адресов $range + * + * @param $ip + * @param $range + * @return bool + */ + private function isIPInV6Range($ip, $range) + { + $firstInRange = inet_pton($range[0]); + $lastInRange = inet_pton($range[1]); + + $ip = inet_pton($ip); + + return (strlen($ip) == strlen($firstInRange)) + && ($ip >= $firstInRange && $ip <= $lastInRange); + } + + /** + * Возвращает список диапазонов IPv4 адресов в формате CIDR и отдельных IPv4 адресов + * с которых Юkassa может отправлять уведомления + * + * @return string[] + */ + final private function getIPv4TrustedList() + { + return array( + '185.71.76.0/27', + '185.71.77.0/27', + '77.75.153.0/25', + '77.75.154.128/25', + '77.75.156.11', + '77.75.156.35', + ); + } + + /** + * Возвращает список диапазонов IPv6 адресов с которых Юkassa может отправлять уведомления + * + * @return \string[][] + */ + final private function getIPv6TrustedList() + { + return array( + array( + '2a02:5180:0000:1509:0000:0000:0000:0000', + '2a02:5180:0000:1509:ffff:ffff:ffff:ffff' + ), + array( + '2a02:5180:0000:2655:0000:0000:0000:0000', + '2a02:5180:0000:2655:ffff:ffff:ffff:ffff' + ), + array( + '2a02:5180:0000:1533:0000:0000:0000:0000', + '2a02:5180:0000:1533:ffff:ffff:ffff:ffff' + ), + array( + '2a02:5180:0000:2669:0000:0000:0000:0000', + '2a02:5180:0000:2669:ffff:ffff:ffff:ffff' + ) + ); + } +} \ No newline at end of file diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/lib/Request/Receipts/ReceiptResponseItemInterface.php b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/lib/Request/Receipts/ReceiptResponseItemInterface.php index c15f1dd..9b4411f 100644 --- a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/lib/Request/Receipts/ReceiptResponseItemInterface.php +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/lib/Request/Receipts/ReceiptResponseItemInterface.php @@ -73,10 +73,22 @@ function getPrice(); */ function getVatCode(); + /** + * Возвращает признак предмета расчета + * @return string|null Признак предмета расчета + */ + function getPaymentSubject(); + + /** + * Возвращает признак способа расчета + * @return string|null Признак способа расчета + */ + function getPaymentMode(); + /** * Возвращает информацию о поставщике товара или услуги * @return SupplierInterface */ function getSupplier(); -} \ No newline at end of file +} diff --git a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/phpdoc.xml b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/phpdoc.xml index 6c023d8..3c90e22 100644 --- a/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/phpdoc.xml +++ b/src/components/com_jshopping/payments/pm_yoomoney/lib/vendor/yoomoney/yookassa-sdk-php/phpdoc.xml @@ -9,7 +9,7 @@ .phpdoc - + latest @@ -39,4 +39,4 @@