Vladimir

Vladimir

С нами с 25 июня 2021; Место в рейтинге пользователей: #283
Vladimir
12 января 2023, 13:53
0
По моему опираясь только на банк(в моём случае) и ждать пока он отправит тоже не то, максимально что я получу от банка, это номер счёта, и максимальная защита это origin запроса.

Сейчас же, в гет запрос должен попасть номер заказа, а так же хэш заказа, а потом ещё и номер заказа, получается защиты будет по больше, ему нужно будет подобрать 2 параметра.

К тому же, по урл ничего не меняется, происходят проверки, и потом лишь только запрашивается у банка код транзакции на стороне сайта, и лишь потом на стороне сайта меняется статус заказа. В том же пейпале точно так же как и у меня, у меня даже по больше защиты будет, разве нет?
Vladimir
12 января 2023, 12:57
0
По урл у меня ничего не обновляется, у меня идёт редирект после оплаты на страницу, сразу с гет запросами стандартными, action, mscode, msorder, а так же добавил свой invoice_id, всё, потом идёт проверки, правильный ли хэш заказа с номером заказа, потом берётся номер счёта на оплату, и спрашивает у банка код этой транзакции, если код транзакции говорит ок, то только тогда я меняю статус.

Так же сейчас добавлю поле для заказа, и буду записывать туда номер счёта на оплату, и так же добавлю новую проверку
Vladimir
12 января 2023, 12:45
0
Сделал по аналогу как в пейпале код ниже. Я поставил чтобы редирект был на этот файл, чтобы получить поля «action» и так далее. Проверяется оплата, проверяется хэш, потом идёт редирект, и получается что всё это время у пользователя грузится страница, секунды 4.

<?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');


function goNext(){
    global $modx;
    $redirect = $_GET['action'] == 'success'
        ? $modx->makeUrl((int) $modx->getOption('epay_post_success_page_id'), '', '', 'full')
        : $modx->makeUrl((int) $modx->getOption('epay_post_error_page_id'), '', '', 'full');
    return $modx->sendRedirect($redirect);
}


if (isset($_GET['action']) && !empty($_GET['action']) && !empty($_GET['msorder']) && !empty($_GET['mscode']) && !empty($_GET['invoice_id'])) {

    /** @var miniShop2 $miniShop2 */
    $miniShop2 = $modx->getService('miniShop2');
    $miniShop2->loadCustomClasses('payment');



    if (!class_exists('EpayHalykBank')) {
        $_GET['action'] = 'cancel';
        goNext();
    }


    /** @var msOrder $order */
    $order = $modx->newObject('msOrder');    
    $handler = new EpayHalykBank($order);


    if ($order = $modx->getObject('msOrder', (int)$_GET['msorder'])) {
        /** @var msPaymentInterface|EpayHalykBank $handler */

 
        $verifyingOrderPaymentStatus = $handler->getOrderPaymentStatus($order, array(
            'action' => $_GET['action'],
            'msorder' => $_GET['msorder'],
            'mscode' => $_GET['mscode'],
            'invoice_id' => $_GET['invoice_id']
        ));

        // $modx->log(
        //     modX::LOG_LEVEL_ERROR,
        //     'Верификация 
'.var_dump($verifyingOrderPaymentStatus['status'])
        // );

        if($verifyingOrderPaymentStatus && !empty($verifyingOrderPaymentStatus['status'])){
            $updatingOrder = $handler->receive($order, array(
                'verifyedResponse' => $verifyingOrderPaymentStatus,
                'requestParams' => array(
                    'action' => $_GET['action'],
                    'msorder' => $_GET['msorder'],
                    'mscode' => $_GET['mscode'],
                    'invoice_id' => $_GET['invoice_id']
                )
            ));
            if($updatingOrder){
                $_GET['action'] = 'success';
            } else {
                $_GET['action'] = 'cancel';
            }
            goNext();
        }        
    } else {
        $_GET['action'] = 'cancel';
        goNext();
    }


}

goNext();
Ниже функции проверки и смены статуса
/**
* @param msOrder $order
* @param array params
*
* @return array|string
*/
public function getOrderPaymentStatus(msOrder $order, $params = array()){

    if($params['action'] == 'continue'){
        $createdOrderHash = $this->getOrderHashOld($order);
        if($params['mscode'] == $createdOrderHash){
            $authResponse = $this->paymentAuth($order);

            if($authResponse['access_token']){
                $getPaymentStatus = $this->jwt_request(
                    $this->config['transactionCheckStatusUrl'].$params['invoice_id'],
                    $authResponse['access_token']
                );

                return array(
                    'status' => 'verified',
                    'response' => $getPaymentStatus
                );

            } else {
                return array(
                    'status' => 'error'
                );
            }


        }
    }

}




/**
* @param msOrder $order
* @param array $params
*
* @return bool
*/
public function receive(msOrder $order, $params = array())
{
    if ($params['requestParams']['action'] == 'continue' && $params['verifyedResponse']['status'] == 'verified') {

        if(!empty($params['verifyedResponse']) && !empty($params['verifyedResponse']['response'])  && !empty($params['verifyedResponse']['response']['resultCode'])){
            
            if ($params['verifyedResponse']['response']['resultCode'] == '100') {
                $this->ms2->changeOrderStatus($order->get('id'), 2); // Set status "paid"
            } else {
                $this->ms2->changeOrderStatus($order->get('id'), 1002); // Set status "Payment error"

                $msOrder->set('payment_response_status', $result);
                $msOrder->save();
                return false;
            }

        }

    } else {
        return false;
    }

    return true;
}
Vladimir
12 января 2023, 12:35
0
А можете пожалуйста подсказать, как отправить это в другой поток, то есть после оплаты не важно какой идёт редирект, а там происходит проверка и смена статуса, так как мне добавить проверку и смену статуса, чтобы оно не мешало пользователю и страница не грузилась там несколько секунд. А сразу редиректила на отдельную страницу.

Потому что от банка вроде должно приходить что якоба оплата прошла именно на сайт, а как бы есть то нет, а так же там только есть номер счёта. Конечно можно добавить поле в заказ и указать айди счёт на оплату и по нему искать так, но слишком много костылей, плюс это всё может приходить то не приходить, то может приходить спустя время, а с помощью редиректа я все данные получаю быстро.

Хостинг обычный виртуальный, php 7.4+ modx 2.7.3
Vladimir
12 января 2023, 09:02
0
Да, спасибо смотрел, буду так и делать
Vladimir
12 января 2023, 08:43
0
У сервиса оплаты минишопа есть такой метод как «receive», как я понял из сущ пейпала, что это метод при котором отправляется запрос на проверку успешной оплаты, как его за тригерить? или он сам тригерится после возвращение на страницу оформления заказа с параметрами, успеха, номера заказа и хэша заказа?
Vladimir
12 января 2023, 08:37
0
Мне нужно наверно сделать как в пейпале, добавить php, в ассет платежей минишопа, и ловить это сообщение, а потом что сделать? вручную менять статус? или есть у минишопа метод для этого после успешной оплаты?
Vladimir
10 января 2023, 11:07
0
Проблема регистрации была в том что я использовал
$modx
а тут нужно было
$this->modx
Vladimir
10 января 2023, 09:17
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

        ));
    }
}
Пытаюсь зарегестрировать, так как в доках, через консоль
if ($miniShop2 = $modx->getService('miniShop2')) {
    $miniShop2->addService('payment', 'EpayHalykBank',
        '{core_path}components/minishop2/custom/payment/epayhalykbank.class.php'
    );
}
Где EpayHalykBank это имя класса оплаты. Но когда хочу добавить этот обработчик, он просто не появляется. Пути проверял, имя файла тоже проверил, он содержит class

Можете пожалуйста подсказать, как исправить?
Vladimir
09 января 2023, 15:26
0
Нужно чтобы именно был редирект на страницу оплаты, а не чтобы заполнение данных происходило через сайт.
Vladimir
09 января 2023, 15:16
0
Сам обработчик пытался сделать, и пытался регистрировать как показано в доках минишопа, но он даже не появляется.
<?php

if (!class_exists('msPaymentInterface')) {
    require_once dirname(__FILE__, 3) . '/minishop2/mspaymenthandler.class.php';
}

class mspEpayHalykBank extends msPaymentHandler implements msPaymentInterface
{

    /**
    * mspEpayHalykBank 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) . 'custom/payment/mspepayhalykbank.php';

        $this->config = array_merge(array(
            'grant_type' => 'client_credentials',
            'debug' => $modx->getOption('epay_debug'),
            'paymentEpayUrl' => $this->config['debug'] == 1 ? 'https://testoauth.homebank.kz/epay2/oauth2/token' : 'https://epay-oauth.homebank.kz/oauth2/token',
            'scope' => 'webapi usermanagement email_send verification statement statistics payment',
            'client_id' => $modx->getOption('epay_client_id'),
            'client_secret' => $modx->getOption('epay_client_secret'),
            'currency' => 'KZT',
            'terminal' => $modx->getOption('epay_terminal_id'),
            'postLink' => $siteUrl.'/core/components/epay/succsess.php',
            'failurePostLink' => $siteUrl.'/core/components/epay/error.php'
        ), $config);
    }


    /**
    * @param msOrder $order
    *
    * @return array|string
    */
    public function send(msOrder $order)
    {
        if ($order->get('status') > 1) {
            return $this->error('ms2_err_status_wrong');
        }
        $params = array_merge(
            $this->config,
            array(
                'invoiceID' => $_POST['invoiceId'],
                'amount' => $_POST['amount'],                
            )
        );


        $response = $this->request($params, $this->getPaymentLink($order));
        if (is_array($response) && !empty($response['access_token'])) {

            return $this->success('', array('redirect' => $this->config['checkoutUrl'] . urlencode($token)));
        } else {
            $this->modx->log(
                modX::LOG_LEVEL_ERROR,
                '[miniShop2] Payment error while request. Request: ' . print_r(
                    $params,
                    1
                ) . ', response: ' . print_r(
                    $response,
                    1
                )
            );

            return $this->success('', array('msorder' => $order->get('id')));
        }
    }



    /**
    * Building query
    *
    * @param array $params Query params
    *
    * @return array/boolean
    */
    public function request($params = array(), $paymentEpayUrlFull)
    {
        $myCurl = curl_init();
        curl_setopt_array($myCurl, array(
            CURLOPT_URL => $paymentEpayUrlFull,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => http_build_query($params)
        ));


        $response = curl_exec($myCurl);
        if (curl_errno($myCurl)) {
            $result = curl_error($myCurl);
        }


        header('Content-type: application/json');

        curl_close($myCurl);

        return $response;
    }


    /**
    * Returns a direct link for continue payment process of existing order
    *
    * @param msOrder $order
    *
    * @return string
    */
    public function getPaymentLink(msOrder $order)
    {
        return $this->config['paymentEpayUrl'] . '?' .
        http_build_query(array(
            'action' => 'continue',
            'msorder' => $order->get('id'),
            'mscode' => $this->getOrderHash($order),
        ));
    }
}
Vladimir
05 января 2023, 09:44
0
Да, спасибо огромное, совсем забыл просто то куда я поставил поле.
Vladimir
05 января 2023, 08:30
0
Как позже можно обновить допустим чекбокс созданный вашим методом?
я пытался так, но не работает, Но текстовое поле обновляется, а вот чекбокс никак
$user = $modx->getObject('modUser', array('id' => $userId));
$profile->set('regular_customer', 1);
$profile->save();
Vladimir
30 декабря 2022, 13:16
0
Решил с помощью данной статьи. modx.pro/solutions/22595

Теперь плагин такой.

<?php
switch($modx->event->name){
    case 'OnMODXInit':
    $map = array(
            'msOrder' => array(
                'fields' => array(
                    'regular_customer' => 0
                ),
                'fieldMeta' => array(
                    'regular_customer' => array(
                    'dbtype' => 'tinyint',
                    'precision' => 1,
                    'attributes' => 'unsigned',
                    'phptype' => 'boolean',
                    'null' => false,
                    'default' => 0,
                    ),
                ),
            ),
        );
        foreach ($map as $class => $data) {
            $modx->loadClass($class);

            foreach ($data as $tmp => $fields) {
                if ($tmp == 'fields') {
                    foreach ($fields as $field => $value) {
                        foreach (array('fields', 'fieldMeta', 'indexes') as $key) {
                            if (isset($data[$key][$field])) {
                                $modx->map[$class][$key][$field] = $data[$key][$field];
                            }
                        }
                    }
                } elseif ($tmp == 'composites' || $tmp == 'aggregates') {
                    foreach ($fields as $alias => $relation) {
                        if (!isset($modx->map[$class][$tmp][$alias])) {
                            $modx->map[$class][$tmp][$alias] = $relation;
                        }
                    }
                }
            }
        }
        break;
    
    case 'msOnManagerCustomCssJs':
        if ($page != 'orders') return;
        $modx->controller->addHtml("
            <script>
                Ext.ComponentMgr.onAvailable('minishop2-window-order-update', function(){
                    let orderTab = this.fields.items[0].items;
                    let obj = {
                        layout: 'column',
                        defaults: {
                            msgTarget: 'under',
                            border: false
                        },
                        anchor: '100%',
                        items: [
                            { 
                                columnWidth: 1,
                                layout: 'form',
                                items:[
                                    {
                                        title: 'Регулярный пользователь',
                                        xtype: 'xcheckbox',
                                        id: 'minishop2-regular-customer',
                                        labelAlign: 'top',
                                        name: 'regular_customer',
                                        fieldLabel: 'Регулярный пользователь',                                        
                                        autoHeight: true,
                                        inputValue: 1,
                                        border: false,
                                        anchor: '100%',
                                        value: this.record.regular_customer
                                    }
                                ]
                            }
                        ]
                    }
                    orderTab.push(obj);
                });                
            </script>");
    break;
}
Vladimir
30 декабря 2022, 12:49
0
Потому что задача стоит такая, чтобы если больше 5тя сделанных заказов, то должна галочка стоят в каждом заказе от этого пользователя. Нет какого-то функционала если больше 5ти заказов, это галочка чисто для менеджеров будет
Vladimir
29 декабря 2022, 08:45
0
Добавил поле, однако оно дублируется, стоит самым первым, а так же 4м, 1 что стоит оно отключено и не возможно набрать, а последнее работает отлично, как убрать дубликат? В админке так же есть уже другие доп поля, сделанные не мной, такого плагина как тут не стояло там.

Мой плагин получился таким.
<?php
switch ($modx->event->name) {
    case "OnMODXInit":
        $map = array(
            'modUser' => array(
                'fields' => array(
                    'regular_customer' => 1,
                ),
                'fieldMeta' => array(
                    'regular_customer' => array(
                        'dbtype' => 'tinyint',
                        'precision' => '1',
                        'phptype' => 'boolean',
                        'attributes' => 'unsigned',
                        'null' => false,
                        'default' => 0,
                    ),
                ),
            )
        );

        foreach ($map as $class => $data) {
            $modx->loadClass($class);

            foreach ($data as $tmp => $fields) {
                if ($tmp == 'fields') {
                    foreach ($fields as $field => $value) {
                        foreach (array('fields', 'fieldMeta', 'indexes') as $key) {
                            if (isset($data[$key][$field])) {
                                $modx->map[$class][$key][$field] = $data[$key][$field];
                            }
                        }
                    }
                } elseif ($tmp == 'composites' || $tmp == 'aggregates') {
                    foreach ($fields as $alias => $relation) {
                        if (!isset($modx->map[$class][$tmp][$alias])) {
                            $modx->map[$class][$tmp][$alias] = $relation;
                        }
                    }
                }
            }
        }
        break;
    
    case "OnUserFormPrerender":
        if (!isset($user) || $user->get('id') < 1) {
            return;
        }

        if (!$modx->getCount('modPlugin', array('name' => 'AjaxManager', 'disabled' => false))) {
            $data['regular_customer'] = $user->regular_customer ? 'true' : 'false';

            $modx->controller->addHtml("
                <script type='text/javascript'>
                    Ext.ComponentMgr.onAvailable('modx-user-tabs', function() {
                        this.on('beforerender', function() {
                            // Получаем колонки первой вкладки
                            var rightCol = this.items.items[0].items.items[0].items.items[1];

                            // Добавляем чекбокс первым по счёту полем (перед чекбоксом 'Активный')
                            rightCol.items.insert(0, 'modx-user-regular-customer', new Ext.form.Checkbox({
                                id: 'modx-user-regular-customer',
                                name: 'regular_customer',
                                hideLabel: true,
                                boxLabel: 'Постоянный покупатель',
                                description: 'Указывает что пользователь сделал более 5ти заказов.',
                                xtype: 'xcheckbox',
                                inputValue: 1,
                                listeners: {
                                    beforerender: function(that) {
                                        that.hiddenField = new Ext.Element(document.createElement('input')).set({
                                            type: 'hidden',
                                            name: that.name,
                                            value: 0,
                                        });
                                    },
                                    afterrender: function(that) {
                                        that.el.insertHtml('beforeBegin', that.hiddenField.dom.outerHTML);
                                    },
                                },
                                checked: {$data['regular_customer']},
                            }));
                        });
                    });
                </script>
            ");
        }
        break;
}
Vladimir
20 декабря 2022, 14:32
0
Суть такая, при клике на кнопку сохраняет корзину, и сразу получает ссылку на корзину, чтобы последующем перейти на страницу эту, и при нажатии на кнопку «Заказать», заказать все товары с текущей сохраненной корзины.
Vladimir
12 декабря 2022, 11:59
+1
Можно это запустить всё через pdoPage и просто указать параметр context, и указать чтобы контекст брался через обращение к текущей страницы, которая сделала запрос, так же через pdoPage можно сделать так чтобы если больше 1к записей, будет 2я страница но с уже другими записями.
<?php
$output = $modx->runSnippet('pdoPage',array(
    'element' => 'pdoSitemap',
    'parents' => 0,
    'context' => $modx->context->key,
    'depth' => 10,
    'limit' => 100,
    'showHidden' => 0
));

return $output;
Ниже сам код, выше пример который работает у меня.
'context' => $modx->context->key,
Vladimir
29 ноября 2022, 07:03
+1
В последние мои зайдите, если не видите причину в минусе, то можете банить, люди не читают вопрос, либо не доконца, и начинают написывать вообще левое, хотябы по стилям где вы сами прокомментировали перед вами человек, это что ответ или помощь? при том что я написал что УЖЕ РЕШИЛ, но люди не понимают этого, и сами не могут ответить и пишут лишь потом, и только по поводу личного моего решения
Vladimir
29 ноября 2022, 06:56
+2
Иван, если вы не понимаете почему я их ставлю видимо вы такой же, можете банить сразу. Я задал вопрос, 1 пишет вообще изначально другое, в других вот недавно поставил минус, чел просто скинул ссылку без объяснений, и вообще не решение никак, или когда я сам нашёл ответ, начинают что-то написывать, не так сделал, так не надо, при этом сами не дают ответ, и все начинают лишь писать после того как я сам найду ответ, баньте, ничего страшного просто будет 5 аккаунт