[miniShop2] Почему не подключается самописный сервис оплаты?

Здравствуйте!
Пытаюсь написать свой способ оплаты для miniShop2 по этой статье
Создаю класс, подключаю его через консоль, в ms2_services сервис прописывается,
{"cart":[],"order":[],"payment":{"epayhalykbank":"{core_path}components\/minishop2\/custom\/payment\/epayhalykbank.class.php"},"delivery":[]}
но класс-обработчик не появляется в выпадашке при создании нового способа оплаты
disk.yandex.ru/d/5o_PXC2oeB8CNw

В чём может быть дело?
(за основу брался вот этот топик, modx.pro/help/23614 но у меня так не срабатывает)
Спасибо за помощь
Giant Dad
20 июня 2023, 12:51
modx.pro
1
377
0

Комментарии: 17

Vladimir
20 июня 2023, 18:14
+1
Вам метод оплаты нужно зарегистрировать в системе, код ниже, код регистрации нужно запускать в консоли, Console это пакет для запуска php кода в админке (https://extras.modx.com/package/console), только моё название оплаты «EpayHalykBank» замените на ваше, а так же название файла «epayhalykbank.class.php» только в конце должно быть ".class.php" обязательно
if ($miniShop2 = $modx->getService('miniShop2')) {
    $miniShop2->addService('payment', 'EpayHalykBank',
        '{core_path}components/minishop2/custom/payment/epayhalykbank.class.php'
    );
}
В целом можете написать в тг @invictusmaneobart
    Giant Dad
    21 июня 2023, 11:18
    0
    я так и делаю, как раз по вашему топику, регистрирую в консоли, указываю свой название класса и файла, в сервисах путь появляется, но в выборе способа оплаты — пусто, только стандартный PayPal (я, кстати, тоже для ХалыкБанка пытаюсь сделать)
      Vladimir
      21 июня 2023, 11:25
      0
      Значит у вас ошибка в коде, либо не совпадают названия, у меня так же было, если у вас не подсвечиваются ошибки php кода, закиньте в любой php sandbox, и проверьте, можете написать в тг или скиньте сюда файлы относящиеся к оплате
        Giant Dad
        21 июня 2023, 11:38
        0
        Ну, вот я Ваш метод беру, вот этот:
        <?php
        
        if (!class_exists('msPaymentInterface')) {
            require_once dirname(__FILE__, 3) . '/handlers/mspaymenthandler.class.php';
        }
        
        class EpayHalykBank extends msPaymentHandler implements msPaymentInterface
        {
        
            /**
            * EpayHalykBank constructor.
            *
            * @param xPDOObject $object
            * @param array $config
            */
            function __construct(xPDOObject $object, $config = array())
            {
                parent::__construct($object, $config);
        
                $siteUrl = $this->modx->getOption('site_url');
                $assetsUrl = $this->modx->getOption('assets_url') . 'components/minishop2/';
                $paymentUrl = $siteUrl . substr($assetsUrl, 1) . 'payment/mspepayhalykbank.php';
        
                $this->config = array_merge(array(
                    'grant_type' => 'client_credentials',
                    'debug' => $modx->getOption('epay_debug'),
                    'epayTokenAuthUrl' => $this->config['debug'] == 1 ? $modx->getOption('epay_test_auth_url') : $modx->getOption('epay_prod_auth_url'),
                    'payPageUrl' => $this->config['debug'] == 1 ? $modx->getOption('epay_test_pay_page_url') : $modx->getOption('epay_pay_page_url'),
                    'scope' => 'webapi usermanagement email_send verification statement statistics payment',
                    'client_id' => $modx->getOption('epay_client_id'),
                    'client_secret' => $modx->getOption('epay_client_secret'),
                    'currency' => $modx->getOption('epay_payment_currency'),
                    'terminal_id' => $modx->getOption('epay_terminal_id'),
                    'postLink' => $modx->makeUrl($modx->getOption('epay_post_success_page_id')),
                    'failurePostLink' => $modx->makeUrl($modx->getOption('epay_post_error_page_id'))
                ), $config);
            }
        
        
        
            /**
            * Create Auth Token
            *
            * @param 
            *
            * @return string
            */
            public function paymentAuth(msOrder $order)
            {
                $authUrl = $this->config['epayTokenAuthUrl'];
        
                $params = array(
                    'grant_type' => $this->config['grant_type'],
                    'scope' => $this->config['scope'],
                    'client_id' => $this->config['client_id'],
                    'client_secret' => $this->config['client_secret'],
                    'invoiceID' => $this->getOrderHash($order),
                    'amount' => $order->get('cost'),
                    'currency' => $this->config['currency'],
                    'terminal' => $this->config['terminal_id'],
                    'postLink' => $this->config['postLink'],
                    'failurePostLink' => $this->config['failurePostLink']
                );
        
                $myCurl = curl_init();
                curl_setopt_array($myCurl, array(
                    CURLOPT_URL => $authUrl,
                    CURLOPT_RETURNTRANSFER => true,
                    CURLOPT_POST => true,
                    CURLOPT_POSTFIELDS => http_build_query($params)
                ));
        
                $response = curl_exec($myCurl);
                curl_close($myCurl);
        
                return json_decode($response, true);
        
            }
        
        
            /**
            * Returns a direct link for continue payment process of existing order
            *
            * @param msOrder $order
            *
            * @return string
            */
            public function getPaymentLink(msOrder $order)
            {
        
                $authResponse = $this->paymentAuth($order);
        
                $msAddress = $order->getOne('Address');
                $orderPhone = $msAddress->get('phone');
                $orderEmail = $msAddress->get('email');
                $orderReceiver = $msAddress->get('receiver');
                $orderComment = $msAddress->get('comment');
        
                if(substr($orderPhone, 0, 1) == '7'){
                    $orderPhone = '+' . $orderPhone;
                }
                // if($authResponse['access_token'])
                // 'data' => "{\"statement\":{\"name\":\"Arman      Ali\",\"invoiceID\":\"80000016\"}}",
        
                return $this->config['payPageUrl'] . '?' .
                http_build_query(array(
                    'action' => 'continue',
                    'msorder' => $order->get('id'),
                    'mscode' => $this->getOrderHash($order),
                    'invoiceId' => $this->getOrderHash($order),
                    'backLink' => $this->config['postLink'].'?action=success',
                    'failureBackLink' => $this->config['failurePostLink'].'?action=error',
                    'postLink' => $this->$siteUrl,
                    'failurePostLink' => $this->config['failurePostLink'].'?action=error',
                    'language' => 'rus',
                    'description' => 'Оплата в интернет магазине',
                    'accountId' => $this->config['client_id'],
                    'terminal' => $this->config['terminal_id'],
                    'amount' => $order->get('cost'),
                    'data' => array(
                        'name' => $orderReceiver,
                        'orderId' => $order->get('id'),
                        'invoiceId' => $this->getOrderHash($order),
                        'phone' => $orderPhone,
                        'email' => $orderEmail,                
                        'comment' => $orderComment
                    ),
                    'currency' => $this->config['currency'],
                    'phone' => $orderPhone,
                    'email' => $orderEmail,
                    'cardSave' => true,
                    'auth' => $authResponse
        
                ));
            }
        }
        Заменяю в
        require_once dirname(__FILE__, 3) . '/handlers/mspaymenthandler.class.php';
        путь на свой:
        require_once dirname(__FILE__, 3) . '/model/minishop2/mspaymenthandler.class.php';
        название класса оставляю прежним,
        регистрирую через
        if ($miniShop2 = $this->modx->getService('miniShop2')) {
            $miniShop2->addService('payment', 'EpayHalykBank',
                '{core_path}components/minishop2/custom/payment/epayhalykbank.class.php'
            );
        }
        Может быть я что-то ещё упускаю?
        И ещё, а вот как это должно вообще работать (хотя бы в общих чертах)? После нажатия кнопки «Оплатить заказ» что должно происходить? в каких файлах какие функции должны отрабатывать? Для меня просто оплата в minishop2 пока новый опыт и я не совсем представляю как это вообще работает
          Vladimir
          21 июня 2023, 11:51
          0
          Почему у вас «model» в пути? какая у вас версия modx

          Всё зависит от работы сервиса оплаты, в случае с epayment, то там запутанои уже не помню точно, но суть такая, на сервер отправляется запрос на получение ключа, формируется заказ в виде словаря, отправляется на фронт и с помощью библиотеки epayment которая указана в доках отправляется заказ, без этой библиотеки(именно js создаёт конечный урл с параметрами, который валиден и позволит оплатить, в тех поддержки сказали что без их js никак) не будет рабоать оплата.
            Giant Dad
            21 июня 2023, 11:52
            0
            версия 2-8-3 pl
              Vladimir
              21 июня 2023, 11:57
              0
              Если вы ничего не меняли в моём файле, то должно работать, я ниже добавил так же файл assets его тоже создайте, и замените model на Handlers так как было у меня, а так же такие методы
              $modx->getOption('epay_debug')
              замените для начало просто строкой, если всё заработает то верните эти конструкции и создайте в системных настройках эти параметры и заполните, могу скинуть скрин как их нужно заполнить
                Giant Dad
                21 июня 2023, 12:02
                0
                спасибо, действительно мне не хватало файла /assets/components/minishop2/payment/epayhalykbank.php
                Сейчас метод оплаты появился, но при непосредственно попытке оформить заказ с этим способом оплаты, возникает ошибка «epayhalykbank.class.php: 215) [miniShop2] Payment error while request. Request:, authResponse: » Насколько я понимаю, это как раз из-за отсутствия настроек подключения. Попробую заполнить вручную. Если не сложно, скиньте скрин, пожалуйста
                  Vladimir
                  21 июня 2023, 12:12
                  0
                  1) prnt.sc/_-mnia_UC4Ie
                  2) prnt.sc/9UWwxd0Cg0DN

                  Те что закрашены вам нужно получить в вашем личном кабинете, а так же их js библиотека важна, вот как у меня работает отправка через их js

                  miniShop2.Callbacks.add('Order.submit.response.success', 'orders_submit_ok', function(responseMinishop) {
                  	
                  	if ($('#payment_4').is(':checked')) {
                  	    
                          if(responseMinishop.data['payment_response']){
                  			halyk.pay(responseMinishop.data['payment_response']);
                          }
                  
                  	}
                  });
                  Если у вас всё же ошибка происходит, то значит эти обезьяны уже что-то изменили кардинально и не сообщили об этом своих клиентов, скорей всего это в методе авторизации, нужно смотреть и разбираться, а эти глупые вблдки из тех поддержки хр вам помогут, и будут отвечать 1-2 раза в день по почте, и такое чувство что там сидят люди ни разу не работавшие с их оплатой.

                  Если у вас получится самим решить напишите сюда, если до вечера не получится, напишите мне в тг я гляну
                    Giant Dad
                    21 июня 2023, 12:16
                    0
                    да уж, с их ТП уже пришлось пообщаться, отвечают раз в день односложными фразами, половину приходится догадываться. ок, если что, напишу, ещё раз огромное спасибо
                      Giant Dad
                      21 июня 2023, 16:58
                      0
                      Получилось решить вопрос с оплатой благодаря Вам. Ещё раз спасибо огромное. Остался всего один момент, как сделать так, чтобы после успешного завершения оплаты, заказ получал статус «оплачен»? Ну, тоесть, я понимаю, что это делается через
                      $miniShop2->changeOrderStatus($orderId, $statusId);
                      но где это прописывать и как прокинуть номер заказа (invoiceId насколько я понимаю) из их ответа пока нет идей
                        Vladimir
                        21 июня 2023, 18:47
                        0
                        При успешной или нет оплаты, метод receive сам всё сделает, вам только нужно заменить айдишки статусов заказа на ваш, в текущем состаоянии как я ниже писал, что при оплате удачной или нет, сам епэй тригернёт файл который лежит в ассетах, со статусом оплаты, если пройдёт верификацю это запрос, то всё само сменится в методе receive
                      Giant Dad
                      21 июня 2023, 18:12
                      0
                      Кстати, как я понимаю, Вы расширили класс msOrder, добавив к нему поле order_hash? Это для того, чтобы сохранять в базе подтверждение оплаты?
                        Vladimir
                        21 июня 2023, 18:20
                        0
                        Да расширил, некий аля защитный механизм, можете убрать везде invoiceId, так как это сугубо моё решение. Ведь всё так же остается сам хэш. А как вы решили проблему?
                        Vladimir
                        21 июня 2023, 18:23
                        0
                        Насколько я помню при отправке запроса на оплату, тригерится метод send, в котором происходит запрос ключа, его верификация создания масива с данными, который летит на фронт, где его подхватывает их библиотека создаёт запрос, затем когда оплата произошла успешно или нет, автоматом от халык банка тригерится тот файл что находит в ассетах, именно он проверяет оплату если успешна то меняет статус.
    Vladimir
    21 июня 2023, 11:35
    1
    +2
    Так же по этому пути должен быть ещё файл
    /assets/components/minishop2/payment/epayhalykbank.php
    /
    <?php
    
    const MODX_API_MODE = true;
    /** @noinspection PhpIncludeInspection */
    require dirname(__FILE__, 5) . '/index.php';
    
    /** @var modX $modx */
    $modx->getService('error', 'error.modError');
    $modx->setLogLevel(modX::LOG_LEVEL_ERROR);
    $modx->setLogTarget('FILE');
    
    
    $isJsonRequest = (json_decode(file_get_contents('php://input')) != NULL) ? true : false;
    
    if ($_SERVER['REQUEST_METHOD'] === 'POST' && $isJsonRequest) {
    
        // $modx->log(modX::LOG_LEVEL_ERROR,'ЗАШЛИ ');
    
        $requestData = json_decode(file_get_contents('php://input'), true);
    
        $get_array = array();
        
        parse_str($requestData['data'], $get_array);
        
        $requestData['data'] = $get_array;
    
    
        if(isset($requestData['invoiceId']) && !empty($requestData['invoiceId']) && isset($requestData['data']) && !empty($requestData['data']) && isset($requestData['data']['orderId']) && !empty($requestData['data']['orderId']) && isset($requestData['data']['orderHash']) && !empty($requestData['data']['orderHash'])){
            /** @var miniShop2 $miniShop2 */
            $miniShop2 = $modx->getService('miniShop2');
            $miniShop2->loadCustomClasses('payment');
    
    
    
            if (!class_exists('EpayHalykBank')) {
                exit('Error payment not exist');
            }
    
    
            /** @var msOrder $order */
            $order = $modx->newObject('msOrder');    
            $handler = new EpayHalykBank($order);
    
            $orderId = $requestData['data']['orderId'];
            $orderHash = $requestData['data']['orderHash'];
            $paymentInvocieId = $requestData['invoiceId'];
    
            if ($order = $modx->getObject('msOrder', array('id' => $orderId, 'order_hash' => $orderHash))) {
                /** @var msPaymentInterface|EpayHalykBank $handler */
                $verifyingOrderPayment = $handler->handlePaymentVerification($order, array(
                    'invoiceID' => $paymentInvocieId,
                    'orderID' => $orderId,
                    'orderHash' => $orderId,
                ));
                if(!$verifyingOrderPayment){
                    die('Error when continuing order');
                }
                die('True');
            } else {
                exit('Error when continuing order, cannot get order');
            }
    
        }
    
    
    
    }
    
    exit('Access Denied');
    А файл что в коре такой, самое последне изменёный
    <?php
    
    if (!class_exists('msPaymentInterface')) {
        require_once dirname(__FILE__, 3) . '/handlers/mspaymenthandler.class.php';
    }
    
    
    class EpayHalykBank extends msPaymentHandler implements msPaymentInterface
    {
    
        /**
        * EpayHalykBank constructor.
        *
        * @param xPDOObject $object
        * @param array $config
        */
        function __construct(xPDOObject $object, $config = array())
        {
            parent::__construct($object, $config);
    
            $siteUrl = $this->modx->getOption('site_url');
            $assetsUrl = $this->modx->getOption('assets_url') . 'components/minishop2/';
            $this->$paymentUrl = $siteUrl . substr($assetsUrl, 1) . 'payment/epayhalykbank.php';
            $this->debugPayment = $this->modx->getOption('epay_debug');
    
            $this->config = array_merge(array(
                'grant_type' => 'client_credentials',
                'epayTokenAuthUrl' => $this->debugPayment == 1 ? $this->modx->getOption('epay_test_auth_url') : $this->modx->getOption('epay_prod_auth_url'),
                'payPageUrl' => $this->debugPayment == 1 ? $this->modx->getOption('epay_test_pay_page_url') : $this->modx->getOption('epay_pay_page_url'),
                'scope' => 'webapi usermanagement email_send verification statement statistics payment',
                'client_id' => $this->modx->getOption('epay_client_id'),
                'client_secret' => $this->modx->getOption('epay_client_secret'),
                'currency' => $this->modx->getOption('epay_payment_currency'),
                'terminal_id' => $this->modx->getOption('epay_terminal_id'),
                'transactionCheckStatusUrl' =>  $this->debugPayment == 1 ? $this->modx->getOption('transaction_chech_test_url') : $this->modx->getOption('transaction_chech_prod_url'),
                'postLink' => $this->modx->makeUrl((int) $this->modx->getOption('epay_post_success_page_id'), '', '', 'full'),
                'failurePostLink' => $this->modx->makeUrl((int) $this->modx->getOption('epay_post_error_page_id'), '', '', 'full')
            ), $config);
        }
    
    
        public function getOrderHashOld(msOrder $order)
        {
            
            return md5(
                $order->get('id') .
                $order->get('num') .
                $order->get('createdon') .
                $order->get('cart_cost') .
                $order->get('delivery_cost')
            );
            
        }
    
        public function getOrderHash(msOrder $order)
        {
            
            return substr(preg_replace('/[^0-9]/', '', md5(
                $order->get('id') .
                $order->get('num') .
                $order->get('createdon') .
                $order->get('cart_cost') .
                $order->get('delivery_cost')
            )), 0, 15);
            
        }
    
    
        /**
        * @param msOrder $order
        * @param array params
        *
        * @return array|string
        */
        public function handlePaymentVerification(msOrder $order, $params = array()){
    
            if(isset($params['invoiceID']) && !empty($params['invoiceID'])){
    
                $generateOrderInoiceId = $this->getOrderHash($order);
    
                if($params['invoiceID'] == $generateOrderInoiceId){
    
                    $authResponse = $this->paymentAuth($order);
                    $verifiedResponse = false;
    
                    if($authResponse['access_token']){
                        $getPaymentStatus = $this->jwt_request(
                            $this->config['transactionCheckStatusUrl'].$generateOrderInoiceId,
                            $authResponse['access_token']
                        );
    
                        $verifiedResponse = $getPaymentStatus;
    
    
                    }
    
    
    
                    return $this->receive($order, array(
                        'verifyedResponse' => $verifiedResponse
                    ));
                }
    
    
            }
    
            return false;
    
        }
    
    
    
    
        /**
        * @param msOrder $order
        * @param array $params
        *
        * @return bool
        */
        public function receive(msOrder $order, $params = array())
        {
            if(!empty($params['verifyedResponse']) && !empty($params['verifyedResponse'])  && !empty($params['verifyedResponse']['resultCode'])){
    
                if ($params['verifyedResponse']['resultCode'] == '100') {
                    $this->ms2->changeOrderStatus($order->get('id'), 2); // Set status "paid"
                    return true;
                } else {
                    $this->ms2->changeOrderStatus($order->get('id'), 1002); // Set status "Payment error"
    
                    $msOrder->set('payment_response_status', $result);
                    $msOrder->save();
                    return false;
                }
    
            }
    
    
    
            return false;
        }
    
    
    
        /**
        * @param msOrder $order
        *
        * @return array|string
        */
        public function send(msOrder $order)
        {
            if ($order->get('status') > 1) {
                return $this->error('ms2_err_status_wrong');
            }
    
    
    
            $authResponse = $this->paymentAuth($order);
    
    
            if (is_array($authResponse) && !empty($authResponse['access_token'])) {
                // $this->modx->log(1, 'OPLATA_NACHALAS');
    
                $msAddress = $order->getOne('Address');
                $orderPhone = $msAddress->get('phone');
                $orderEmail = $msAddress->get('email');
                $orderReceiver = $msAddress->get('receiver');
    
                if(substr($orderPhone, 0, 1) == '7'){
                    $orderPhone = '+' . $orderPhone;
                }
    
                $orderHash = $this->getOrderHash($order);
    
                // $this->modx->log(1, $this->getOrderHash($order));
                
                $backLinkParams = '&msorder='.$order->get('id').'&mscode='.$this->getOrderHashOld($order).'&invoice_id='.$orderHash;
                
    
                $reqParams = array(
                    'invoiceId' => $orderHash,
                    'backLink' => $this->config['postLink'].'?action=success'.$backLinkParams,
                    'failureBackLink' => $this->$paymentUrl,
                    'postLink' => $this->$paymentUrl,
                    'failurePostLink' => $this->$paymentUrl,
                    'language' => 'rus',
                    'description' => 'Оплата в интернет магазине',
                    'accountId' => $this->config['client_id'],
                    'terminal' => $this->config['terminal_id'],
                    'amount' => $order->get('cost'),
                    'data' => array(
                        'name' => $orderReceiver,
                        'orderId' => $order->get('id'),
                        'orderHash' => $this->getOrderHashOld($order),
                        'invoiceId' => $orderHash,
                        'phone' => $orderPhone,
                        'email' => $orderEmail
                    ),
                    'currency' => $this->config['currency'],
                    'phone' => $orderPhone,
                    'email' => $orderEmail,
                    'cardSave' => true,
                    'auth' => $authResponse
    
                );
    
                return $this->success('', array('payment_response' => $reqParams));
            } else {
                $this->modx->log(
                    modX::LOG_LEVEL_ERROR,
                    '[miniShop2] Payment error while request. Request: ' . print_r(
                        $params,
                        1
                    ) . ', authResponse: ' . print_r(
                        $authResponse,
                        1
                    )
                );
    
                return $this->success('', array('msorder' => $order->get('id')));
            }
        }
    
    
    
    
        private function jwt_request($url, $token) {
    
            header('Content-Type: application/json'); // Specify the type of data
            $ch = curl_init($url); // Initialise cURL
            $authorization = "Authorization: Bearer ".$token; // Prepare the authorisation token
            curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json' , $authorization )); // Inject the token into the header
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // This will follow any redirects
            $result = curl_exec($ch); // Execute the cURL statement
            curl_close($ch); // Close the cURL connection
            return json_decode($result, true); // Return the received data
    
        }
    
    
        /**
        * Create Auth Token
        *
        * @param 
        *
        * @return string
        */
        public function paymentAuth(msOrder $order)
        {
            $authUrl = $this->config['epayTokenAuthUrl'];
            
            $params = array(
                'grant_type' => $this->config['grant_type'],
                'scope' => $this->config['scope'],
                'client_id' => $this->config['client_id'],
                'client_secret' => $this->config['client_secret'],
                'invoiceID' => $this->getOrderHash($order),
                'amount' => $order->get('cost'),
                'currency' => $this->config['currency'],
                'terminal' => $this->config['terminal_id'],
                'postLink' => $this->$paymentUrl,
                'failurePostLink' => $this->$paymentUrl
            );
    
            $myCurl = curl_init();
            curl_setopt_array($myCurl, array(
                CURLOPT_URL => $authUrl,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_POST => true,
                CURLOPT_POSTFIELDS => http_build_query($params)
            ));
    
            $response = curl_exec($myCurl);
            curl_close($myCurl);
            // $this->modx->log(1, $response);
            return json_decode($response, true);
    
        }
    
    
        /**
        * Returns a direct link for continue payment process of existing order
        *
        * @param msOrder $order
        *
        * @return string
        */
        public function getPaymentLink(msOrder $order)
        {
    
            $redirectPaymentUrl = $this->config['payPageUrl'];
    
            return $redirectPaymentUrl;
        }
    }
      Pandemic
      05 декабря 2023, 11:41
      0
      Как вы в итоге решили новую проблему? у меня ошибка появляется такая:
      (
          [code] => 138
          [message] => Secret incorrect error
      )
        Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
        17