Plugin Directory

Changeset 2889217


Ignore:
Timestamp:
03/29/2023 12:43:42 PM (3 years ago)
Author:
paygine
Message:

added halva, refund, complete

Location:
paygine/trunk
Files:
24 added
2 edited

Legend:

Unmodified
Added
Removed
  • paygine/trunk/paygine-payment_method.php

    r2792859 r2889217  
    11<?php
    2 /* 
     2declare(strict_types=1);
     3
     4
     5/*
    36    This program is free software; you can redistribute it and/or modify
    4     it under the terms of the GNU General Public License, version 2, as 
     7    it under the terms of the GNU General Public License, version 2, as
    58    published by the Free Software Foundation.
    69
     
    1821 * Plugin URI: http://paygine.ru/
    1922 * Description: Receive payments via Visa/Mastercard easily with Paygine bank cards processing
    20  * Version: 1.1.6
     23 * Version: 2.0
    2124 * Author: Paygine
    22  * Tested up to: 6.1
     25 * Tested up to: 6.1.1
    2326 * License: GPL3
    2427 *
     
    3134
    3235if (false) {
    33     __('Paygine payment method (Visa/MasterCard)');
    34     __('Receive payments via Visa/Mastercard easily with Paygine bank cards processing');
     36    __('Paygine payment method (Visa/MasterCard)');
     37    __('Receive payments via Visa/Mastercard easily with Paygine bank cards processing');
    3538}
    3639
     
    3942function init_woocommerce_paygine()
    4043{
    41     if (!class_exists('WC_Payment_Gateway')) {
    42         return;
    43     }
    44 
    45     load_plugin_textdomain('paygine-payment_method', false, dirname(plugin_basename(__FILE__)) . '/languages');
    46 
    47     class woocommerce_paygine extends WC_Payment_Gateway
    48     {
    49 
    50         public function __construct()
    51         {
    52             $this->id = 'paygine';
    53             $this->method_title = __('Paygine', 'paygine-payment_method');
    54             $this->title = __('Paygine', 'paygine-payment_method');
    55             $this->description = __("Payments with bank cards via the <a href=\"http://www.paygine.ru\" target=\"_blank\">Paygine</a> payment system.", 'paygine-payment_method');
    56             $this->icon = plugins_url('paygine.png', __FILE__);
    57             $this->has_fields = true;
    58             $this->notify_url = add_query_arg('wc-api', 'paygine_notify', home_url('/'));
    59             $this->callback_url = add_query_arg('wc-api', 'paygine', home_url('/'));
    60 
    61             $this->init_form_fields();
    62             $this->init_settings();
    63 
    64             // variables
    65             $this->sector = $this->settings['sector'];
    66             $this->password = $this->settings['password'];
    67             $this->testmode = $this->settings['testmode'];
    68             $this->twostepsmode = $this->settings['twostepsmode'];
    69 
    70             // actions
    71             add_action('init', array($this, 'successful_request'));
    72             add_action('woocommerce_api_paygine', array($this, 'callback_from_gateway'));
    73             add_action('woocommerce_api_paygine_notify', array($this, 'notify_from_gateway'));
    74             add_action('woocommerce_update_options_payment_gateways_' . $this->id, array($this, 'process_admin_options'));
    75         }
    76 
    77         /**
    78          * Admin Panel Options
    79          **/
    80         public function admin_options()
    81         {
    82             ?>
    83             <h3><?php _e('Paygine', 'paygine-payment_method'); ?></h3>
    84             <p><?php _e("Payments with bank cards via the <a href=\"http://www.paygine.ru\" target=\"_blank\">Paygine</a> payment system.", 'paygine-payment_method'); ?></p>
    85             <table class="form-table">
    86                 <?php
    87                 // Generate the HTML For the settings form.
    88                 $this->generate_settings_html();
    89                 ?>
    90             </table><!--/.form-table-->
    91             <?php
    92         }
    93 
    94         /**
    95          * Initialise Gateway Settings Form Fields
    96          */
    97         public function init_form_fields()
    98         {
    99 
    100             //  array to generate admin form
    101             $this->form_fields = array(
    102                 'enabled' => array(
    103                     'title' => __('Enable/Disable', 'paygine-payment_method'),
    104                     'type' => 'checkbox',
    105                     'label' => __('Enable Paygine checkout method', 'paygine-payment_method'),
    106                     'default' => 'yes'
    107                 ),
    108 
    109                 'sector' => array(
    110                     'title' => __('Sector ID', 'paygine-payment_method'),
    111                     'type' => 'text',
    112                     'description' => __('Your shop identifier at Paygine', 'paygine-payment_method'),
    113                     'default' => 'test'
    114                 ),
    115 
    116                 'password' => array(
    117                     'title' => __('Password', 'paygine-payment_method'),
    118                     'type' => 'text',
    119                     'description' => __('Password to use for digital signature', 'paygine-payment_method'),
    120                     'default' => 'test'
    121                 ),
    122 
    123                 'testmode' => array(
    124                     'title' => __('Test Mode', 'paygine-payment_method'),
    125                     'type' => 'select',
    126                     'options' => array(
    127                         '1' => 'Test mode - real payments will not be processed',
    128                         '0' => 'Production mode - payments will be processed'
    129                     ),
    130                     'description' => __('Select test or live mode', 'paygine-payment_method')
    131                 ),
    132                 'twostepsmode' => array(
    133                     'title' => __('2 steps payment mode', 'paygine-payment_method'),
    134                     'type' => 'select',
    135                     'options' => array(
    136                         '1' => 'On',
    137                         '0' => 'Off'
    138                     ),
    139                     'description' => __('Turn on 2 steps mode', 'paygine-payment_method')
    140                 ),
    141                 'deferred' => array(
    142                     'title' => __('Deferred payment', 'paygine-payment_method'),
    143                     'type' => 'select',
    144                     'options' => array(
    145                         '1' => 'On',
    146                         '0' => 'Off'
    147                     )
    148                 )
    149             );
    150 
    151         }
    152 
    153         /**
    154          * Register order @ Paygine and redirect user to payment form
    155          **/
    156         public function process_payment($order_id)
    157         {
    158             $order = new WC_Order($order_id);
    159             switch ($order->get_currency()) {
    160                 case 'EUR':
    161                     $currency = '978';
    162                     break;
    163                 case 'USD':
    164                     $currency = '840';
    165                     break;
    166                 default:
    167                     $currency = '643';
    168                     break;
    169             }
    170 
    171             $paygine_url = "https://test.paygine.com";
    172             if ($this->testmode == "0")
    173                 $paygine_url = "https://pay.paygine.com";
    174             $paygine_operation = "Purchase";
    175             if ($this->twostepsmode == "1")
    176                 $paygine_operation = "Authorize";
    177 
    178             $signature = base64_encode(md5($this->sector . intval($order->get_total() * 100) . $currency . $this->password));
    179 
    180             $wc_order = wc_get_order($order_id);
    181             $items = $wc_order->get_items();
    182             $fiscalPositions = '';
    183             $fiscalAmount = 0;
    184             $KKT = true;
    185 
    186             if ($KKT) {
    187                 foreach ($items as $item_id => $item) {
    188                     $item_data = $item->get_data();
    189                     $fiscalPositions .= $item_data['quantity'] . ';';
    190                     $elementPrice = $item_data['total'] / $item_data['quantity'];
    191                     $elementPrice = $elementPrice * 100;
    192                     $fiscalPositions .= $elementPrice . ';';
    193                     $fiscalPositions .= ($item_data['total_tax']) ?: 6 . ';';   // tax
    194                     $fiscalPositions .= str_ireplace([';', '|'], '', $item_data['name']) . '|';
    195 
    196                     $fiscalAmount += $item_data['quantity'] * $elementPrice;
    197                 }
    198                 if ($wc_order->get_shipping_total()) {
    199                     $fiscalPositions .= '1;' . $wc_order->get_shipping_total() * 100 . ';6;Доставка|';
    200                     $fiscalAmount += $wc_order->get_shipping_total() * 100;
    201                 }
    202                 $fiscalDiff = abs($fiscalAmount - intval($order->get_total() * 100));
    203                 if ($fiscalDiff) {
    204                     $fiscalPositions .= '1;' . $fiscalDiff . ';6;Скидка;14|';
    205                 }
    206                 $fiscalPositions = substr($fiscalPositions, 0, -1);
    207             }
    208 
    209             $args = array(
    210                 'body' => array(
    211                     'sector' => $this->sector,
    212                     'reference' => $order->get_id(),
    213                     'amount' => intval($order->get_total() * 100),
    214                     'fiscal_positions' => $fiscalPositions,
    215                     'description' => sprintf(__('Order #%s', 'paygine-payment_method'), ltrim($order->get_order_number(), '#')),
    216                     'email' => $order->get_billing_email(),
    217                     'notify_customer' => ($this->deferred) ? 1 : 0,
    218                     'currency' => $currency,
    219                     'mode' => 1,
    220                     'url' => $this->callback_url,
    221                     'signature' => $signature
    222                 )
    223             );
    224             $remote_post = wp_remote_post($paygine_url . '/webapi/Register', $args);
    225             $remote_post = (isset($remote_post['body'])) ? $remote_post['body'] : $remote_post;
    226             $paygine_order_id = ($remote_post) ? $remote_post : null;
    227 
    228             if (intval($paygine_order_id) == 0) {
    229                 $request_body = $args['body'];
    230                 $request_body['email'] = ($request_body['email']) ? 'isset' : 'isnotset';
    231                 $request_body['signature'] = ($request_body['signature']) ? 'isset' : 'isnotset';
    232                 $this->logIt('Не удалось зарегистрировать заказ', array('paygine_order_id' => $paygine_order_id, 'request_body' => $request_body));
    233                 return false;
    234             }
    235 
    236             $signature = base64_encode(md5($this->sector . $paygine_order_id . $this->password));
    237 
    238             // $order->update_status('on-hold');
    239 
    240             if ($this->deferred) {
    241                 wp_redirect($this->get_return_url(wc_get_order($response->reference)));
    242                 exit();
    243             }
    244 
    245             return array(
    246                 'result' => 'success',
    247                 'redirect' => "{$paygine_url}/webapi/{$paygine_operation}?sector={$this->sector}&id={$paygine_order_id}&signature={$signature}"
    248             );
    249         }
    250 
    251         /**
    252          * Callback from payment gateway was received
    253          **/
    254         public function callback_from_gateway()
    255         {
    256             // check payment status
    257             $paygine_order_id = intval($_REQUEST["id"]);
    258             if (!$paygine_order_id)
    259                 return false;
    260 
    261             $paygine_operation_id = intval($_REQUEST["operation"]);
    262             if (!$paygine_operation_id) {
    263                 $order_id = intval($_REQUEST["reference"]);
    264                 $order = wc_get_order($order_id);
    265                 if ($order)
    266                     $order->cancel_order(__("The order wasn't paid.", 'paygine-payment_method'));
    267 
    268                 wc_add_notice(__("The order wasn't paid.", 'paygine-payment_method'), 'error');
    269                 $get_checkout_url = apply_filters('woocommerce_get_checkout_url', WC()->cart->get_checkout_url());
    270                 wp_redirect($get_checkout_url);
    271                 exit();
    272             }
    273 
    274             // check payment operation state
    275             $signature = base64_encode(md5($this->sector . $paygine_order_id . $paygine_operation_id . $this->password));
    276 
    277             $paygine_url = "https://test.paygine.com";
    278             if ($this->testmode == "0")
    279                 $paygine_url = "https://pay.paygine.com";
    280 
    281             $context = stream_context_create(array(
    282                 'http' => array(
    283                     'header' => "Content-type: application/x-www-form-urlencoded\r\n",
    284                     'method' => 'POST',
    285                     'content' => http_build_query(array(
    286                         'sector' => $this->sector,
    287                         'id' => $paygine_order_id,
    288                         'operation' => $paygine_operation_id,
    289                         'signature' => $signature
    290                     )),
    291                 )
    292             ));
    293 
    294             $repeat = 3;
    295 
    296             while ($repeat) {
    297 
    298                 $repeat--;
    299 
    300                 // pause because of possible background processing in the Paygine
    301                 sleep(2);
    302                 $args = array(
    303                     'body' => array(
    304                         'sector' => $this->sector,
    305                         'id' => $paygine_order_id,
    306                         'operation' => $paygine_operation_id,
    307                         'signature' => $signature
    308                     )
    309                 );
    310                 $xml = wp_remote_post($paygine_url . '/webapi/Operation', $args)['body'];
    311 
    312                 if (!$xml)
    313                     break;
    314                 $xml = simplexml_load_string($xml);
    315                 if (!$xml)
    316                     break;
    317                 $response = json_decode(json_encode($xml));
    318                 if (!$response)
    319                     break;
    320                 if (!$this->orderAsPayed($response))
    321                     continue;
    322 
    323                 wp_redirect($this->get_return_url(wc_get_order($response->reference)));
    324                 exit();
    325 
    326             }
    327 
    328             $order_id = intval($response->reference);
    329             $order = wc_get_order($order_id);
    330             if ($order)
    331                 $order->cancel_order(__("The order wasn't paid [1]: " . $response->message . '.', 'paygine-payment_method'));
    332 
    333             wc_add_notice(__("The order wasn't paid [1]: ", 'paygine-payment_method') . $response->message . '.', 'error');
    334             $get_checkout_url = apply_filters('woocommerce_get_checkout_url', WC()->cart->get_checkout_url());
    335             wp_redirect($get_checkout_url);
    336             exit();
    337 
    338         }
    339 
    340         /**
    341          * Payment notify from gateway was received
    342          **/
    343         public function notify_from_gateway()
    344         {
    345             global $wp_filesystem;
    346             if (empty($wp_filesystem)) {
    347                 require_once(ABSPATH . '/wp-admin/includes/file.php');
    348                 WP_Filesystem();
    349             }
    350 
    351             // $xml = file_get_contents("php://input");
    352             $xml = $wp_filesystem->get_contents('php://input');
    353             if (!$xml)
    354                 return false;
    355             $xml = simplexml_load_string($xml);
    356             if (!$xml)
    357                 return false;
    358             $response = json_decode(json_encode($xml));
    359             if (!$response)
    360                 return false;
    361 
    362             if (!$this->orderAsPayed($response)) {
    363                 $order_id = intval($response->reference);
    364                 $order = wc_get_order($order_id);
    365                 if ($order)
    366                     $order->cancel_order(__("The order wasn't paid [2]: ", 'paygine-payment_method') . $response->message . '.');
    367                 exit();
    368             }
    369 
    370             die("ok");
    371 
    372         }
    373 
    374         private function orderAsPayed($response)
    375         {
    376             // looking for an order
    377             $order_id = intval($response->reference);
    378             if ($order_id == 0)
    379                 return false;
    380 
    381             $order = wc_get_order($order_id);
    382             if (!$order)
    383                 return false;
    384 
    385             // check payment state
    386             if (($response->type != 'PURCHASE' && $response->type != 'EPAYMENT' && $response->type != 'AUTHORIZE') || $response->state != 'APPROVED')
    387                 return false;
    388 
    389             // check server signature
    390             $tmp_response = json_decode(json_encode($response), true);
    391             unset($tmp_response["signature"]);
    392             unset($tmp_response["ofd_state"]);
    393             unset($tmp_response["protocol_message"]);
    394 
    395             $signature = base64_encode(md5(implode('', $tmp_response) . $this->password));
    396 
    397             if ($signature !== $response->signature) {
    398                 $order->update_status('fail', $response->message);
    399                 return false;
    400             }
    401 
    402             $order->add_order_note(__('Payment completed.', 'paygine-payment_method'));
    403             $order->payment_complete();
    404 
    405             // echo '<pre>' . print_r($tmp_response, true) . '<br>' . $signature . '<br>' . print_r($response, true); die();
    406 
    407             /*
    408              * сохраним в мета заказа выбранный на момент оплаты режим (1 или 2 стадии)
    409              */
    410             $paygine_mode = ($this->settings['twostepsmode']) ? 2 : 1;
    411             update_post_meta($order_id, 'paygine_payment_mode', $paygine_mode);
    412 
    413             return true;
    414 
    415         }
    416 
    417 
    418         /**
    419          * @param string $message
    420          * @param array $details
    421          */
    422         public function logIt (string $message, array $details = []) {
    423             $log = fopen($this->getLogFileName(), 'a+') or die("logging trouble");
    424             $date = date("d.m.y H:i:s");
    425             $msg = $date . "\t" . $message;
    426             if ($details) {
    427                 $msg .= "\n" . print_r($details, true);
    428             }
    429             $msg .= "\n\n\n";
    430             fprintf($log, chr(0xEF).chr(0xBB).chr(0xBF));
    431             fwrite($log, $msg);
    432             fclose($log);
    433         }
    434 
    435         public function getLogFileName () {
    436             $signature = base64_encode($this->sector . $this->password . $_SERVER['HTTP_HOST']);
    437             $signature = str_ireplace('=', '', $signature);
    438             return 'paygine_log_' . $signature . '.txt';
    439         }
    440 
    441 
    442     } // class
    443 
    444     function add_paygine_gateway($methods)
    445     {
    446         $methods[] = 'woocommerce_paygine';
    447         return $methods;
    448     }
    449 
    450     add_filter('woocommerce_payment_gateways', 'add_paygine_gateway');
     44    if(!class_exists('WC_Payment_Gateway')){
     45        return;
     46    }
     47   
     48    load_plugin_textdomain('paygine-payment_method', false, dirname(plugin_basename(__FILE__)) . '/languages');
     49   
     50    class woocommerce_paygine extends WC_Payment_Gateway
     51    {
     52        protected array $pay_states = ['PURCHASE', 'PURCHASE_BY_QR', 'AUTHORIZE', 'COMPLETE'];
     53        protected array $cancel_types = ['REVERSE'];
     54       
     55        public function __construct()
     56        {
     57            $this->id = 'paygine';
     58            $this->method_title = __('Paygine', 'paygine-payment_method');
     59            $this->title = __('Paygine', 'paygine-payment_method');
     60            $this->description = __("Payments with bank cards via the <a href=\"http://www.paygine.ru\" target=\"_blank\">Paygine</a> payment system.", 'paygine-payment_method');
     61            $this->icon = plugins_url('paygine.png', __FILE__);
     62            $this->has_fields = true;
     63            $this->notify_url = add_query_arg('wc-api', 'paygine_notify', home_url('/'));
     64            $this->callback_url = add_query_arg('wc-api', 'paygine', home_url('/'));
     65           
     66            $this->supports = array('refunds', 'products');
     67           
     68            $this->init_form_fields();
     69            $this->init_settings();
     70           
     71            // variables
     72            $this->sector = $this->settings['sector'];
     73            $this->password = $this->settings['password'];
     74            $this->testmode = $this->settings['testmode'];
     75            $this->twostepsmode = $this->settings['twostepsmode'];
     76           
     77            //statuses
     78            $this->status_completed = $this->settings['custom_successfully_paid'];
     79            $this->status_registered = $this->settings['custom_registered'];
     80            $this->status_authorized = $this->settings['custom_authorized'];
     81            $this->status_cancelled = $this->settings['custom_cancelled'];
     82           
     83            $this->mode = $this->settings['mode'];
     84            switch ($this->mode) {
     85                case 1:
     86                    $this->title = 'Оплатить заказ Частями';
     87                    $this->icon = plugins_url('halva.svg', __FILE__);
     88                    $this->description = '<iframe style="width:100%;height:180px;border:none;" src="'.plugins_url('halva_widget.php', __FILE__).'?amount='.print_r((WC()->cart->cart_contents_total) ?: '', true).'"></iframe>';
     89                    break;
     90                case 2:
     91                    $this->title = 'Оплатить по QR';
     92                    $this->icon = null;
     93                    break;
     94            }
     95            // actions
     96            add_action('init', array($this, 'successful_request'));
     97            add_action('woocommerce_api_paygine', array($this, 'callback_from_gateway'));
     98            add_action('woocommerce_api_paygine_notify', array($this, 'notify_from_gateway'));
     99            add_action('woocommerce_api_paygine_complete', array($this, 'paygine_make_complete'));
     100            add_action('woocommerce_update_options_payment_gateways_' . $this->id, array($this, 'process_admin_options'));
     101            add_action('woocommerce_order_item_add_action_buttons', array($this, 'wc_order_item_add_complete_button'), 10, 1);
     102        }
     103       
     104        function wc_order_item_add_complete_button($order)
     105        {
     106            $label = esc_html__('Complete', 'woocommerce');
     107            $slug = 'custom';
     108            $custom_status = $this->status_authorized;
     109            $nonce_complete = wp_create_nonce('paygine_complete' . $order->get_id());
     110            if($order->get_payment_method() == 'paygine'){
     111                if('wc-' . $order->get_status() == $custom_status){
     112                    ?>
     113                    <script src="<?php echo plugins_url('js/scripts.js', plugin_basename(__FILE__)); ?>"></script>
     114                    <input type="hidden" id="nonce_paygine_complete" value="<?php echo $nonce_complete; ?>">
     115                    <button type="button" id="button_paygine_complete"
     116                            class="button <?php echo $slug; ?>-items"><?php echo $label; ?></button>
     117                    <?php
     118                }
     119            }
     120        }
     121       
     122        public function paygine_make_complete()
     123        {
     124            $nonce_complete = $_REQUEST['paygine_nonce_value'];
     125            $order_id = $_REQUEST['order_id'];
     126            if(wp_verify_nonce($nonce_complete, 'paygine_complete' . $order_id)){
     127                $order = wc_get_order($order_id);
     128                $pg_order_id = get_post_meta($order_id, 'paygine_order_id', true);
     129                $currency = $this->convert_currency();
     130                $paygine_url = $this->paygine_get_url();
     131                $paygine_payment_method = substr(get_post_meta($order_id, 'paygine_payment_method', true), 0, 6);
     132                $paygine_operation = ($paygine_payment_method == 'custom') ? 'custom/svkb/Complete' : 'Complete';
     133                $signature = $this->make_signature([$this->sector, $pg_order_id, intval($order->get_total() * 100), $currency]);
     134               
     135                $args = array(
     136                    'body' => array(
     137                        'sector' => $this->sector,
     138                        'id' => $pg_order_id,
     139                        'amount' => intval($order->get_total() * 100),
     140                        'currency' => $currency,
     141                        'signature' => $signature
     142                    )
     143                );
     144                $remote_url = $paygine_url . '/webapi/' . $paygine_operation;
     145                $remote_post = wp_remote_post($remote_url, $args);
     146                $current_status = json_decode(json_encode(simplexml_load_string($remote_post['body'])), true)["order_state"];
     147               
     148                $remote_post = json_encode(simplexml_load_string($remote_post['body']));
     149               
     150                if($current_status == "COMPLETED"){
     151                    $order->update_status(strtolower($current_status));
     152                }
     153                update_post_meta($order_id, 'paygine_payment_status', strtolower($current_status));
     154               
     155                echo $remote_post;
     156            }
     157            exit();
     158        }
     159       
     160        /**
     161         * Set custom status
     162         */
     163        public function order_status_filter($order_statuses, $status_name, $custom_status_name)
     164        {
     165            foreach($order_statuses as $key => $status) {
     166                if($status_name === $key){
     167                    $order_statuses[$status_name] = _x($custom_status_name, 'Order status', 'woocommerce');
     168                }
     169            }
     170            return $order_statuses;
     171        }
     172       
     173        /**
     174         * Admin Panel Options
     175         **/
     176        public function admin_options()
     177        {
     178            ?>
     179            <h3><?php _e('Paygine', 'paygine-payment_method'); ?></h3>
     180            <p><?php _e("Payments with bank cards via the <a href=\"http://www.paygine.ru\" target=\"_blank\">Paygine</a> payment system.", 'paygine-payment_method'); ?></p>
     181            <table class="form-table">
     182                <?php
     183                // Generate the HTML For the settings form.
     184                $this->generate_settings_html();
     185                ?>
     186            </table><!--/.form-table-->
     187            <?php
     188        }
     189       
     190        /**
     191         * Initialise Gateway Settings Form Fields
     192         */
     193        public function init_form_fields()
     194        {
     195            $wc_statuses = wc_get_order_statuses();
     196            //  array to generate admin form
     197            $this->form_fields = array(
     198                'enabled' => array(
     199                    'title' => __('Enable/Disable', 'paygine-payment_method'),
     200                    'type' => 'checkbox',
     201                    'label' => __('Enable Paygine checkout method', 'paygine-payment_method'),
     202                    'default' => 'yes'
     203                ),
     204               
     205                'sector' => array(
     206                    'title' => __('Sector ID', 'paygine-payment_method'),
     207                    'type' => 'text',
     208                    'description' => __('Your shop identifier at Paygine', 'paygine-payment_method'),
     209                    'default' => 'test'
     210                ),
     211               
     212                'password' => array(
     213                    'title' => __('Password', 'paygine-payment_method'),
     214                    'type' => 'text',
     215                    'description' => __('Password to use for digital signature', 'paygine-payment_method'),
     216                    'default' => 'test'
     217                ),
     218               
     219                'testmode' => array(
     220                    'title' => __('Test Mode', 'paygine-payment_method'),
     221                    'type' => 'select',
     222                    'options' => array(
     223                        '1' => __('Test mode - real payments will not be processed', 'paygine-payment_method'),
     224                        '0' => __('Production mode - payments will be processed', 'paygine-payment_method')
     225                    ),
     226                    'description' => __('Select test or live mode', 'paygine-payment_method')
     227                ),
     228               
     229                'mode' => array(
     230                    'title' => __('Режим оплаты', 'paygine-payment_method'),
     231                    'type' => 'select',
     232                    'options' => array(
     233                        '0' => __('Стандартный эквайринг', 'paygine-payment_method'),
     234                        '1' => __('Только Халва.Частями', 'paygine-payment_method'),
     235                        '2' => __('Только СБП', 'paygine-payment_method')
     236                    ),
     237                    'description' => __('Выберите режим оплаты', 'paygine-payment_method')
     238                ),
     239                'twostepsmode' => array(
     240                    'title' => __('2 steps payment mode', 'paygine-payment_method'),
     241                    'type' => 'select',
     242                    'options' => array(
     243                        '1' => __('On', 'paygine-payment_method'),
     244                        '0' => __('Off', 'paygine-payment_method')
     245                    ),
     246                    'description' => __('Turn on 2 steps mode', 'paygine-payment_method')
     247                ),
     248                /*'deferred' => array(
     249                    'title' => __('Deferred payment', 'paygine-payment_method'),
     250                    'type' => 'select',
     251                    'options' => array(
     252                        '1' => 'On',
     253                        '0' => 'Off'
     254                    ),
     255          'description' => __('Making of an email message with a link to payment (payment is not carried out)', 'paygine-payment_method')
     256      )*/
     257                //statuses
     258                'custom_registered' => array(
     259                    'title' => __('Order registered', 'paygine-payment_method'),
     260                    'type' => 'select',
     261                    'options' => $wc_statuses,
     262                ),
     263                'custom_authorized' => array(
     264                    'title' => __('Order authorized', 'paygine-payment_method'),
     265                    'type' => 'select',
     266                    'options' => $wc_statuses,
     267                ),
     268                'custom_successfully_paid' => array(
     269                    'title' => __('Order successfully paid', 'paygine-payment_method'),
     270                    'type' => 'select',
     271                    'options' => $wc_statuses,
     272                ),
     273                'custom_cancelled' => array(
     274                    'title' => __('Order cancelled', 'paygine-payment_method'),
     275                    'type' => 'select',
     276                    'options' => $wc_statuses,
     277                )
     278            );
     279           
     280        }
     281       
     282        public function convert_currency(): string
     283        {
     284            $currency = get_woocommerce_currency();
     285            switch($currency) {
     286                case 'EUR':
     287                    $currency = '978';
     288                    break;
     289                case 'USD':
     290                    $currency = '840';
     291                    break;
     292                default:
     293                    $currency = '643';
     294                    break;
     295            }
     296            return $currency;
     297        }
     298       
     299        /**
     300         * Register order @ Paygine and redirect user to payment form
     301         **/
     302        public function process_payment($order_id)
     303        {
     304            $order = new WC_Order($order_id);
     305            $currency = $this->convert_currency();
     306           
     307            $paygine_url = $this->paygine_get_url();
     308           
     309            switch (intval($this->mode)) {
     310                case 1:
     311                    $paygine_operation = $this->twostepsmode ? 'custom/svkb/AuthorizeWithInstallment' : 'custom/svkb/PurchaseWithInstallment';
     312                    break;
     313                case 2:
     314                    $paygine_operation = 'PurchaseSBP';
     315                    break;
     316                default:
     317                    $paygine_operation = "Purchase";
     318                    if ($this->twostepsmode == "1")
     319                        $paygine_operation = "Authorize";
     320            }
     321           
     322            $signature = $this->make_signature([$this->sector, intval($order->get_total() * 100), $currency]);
     323           
     324            $wc_order = wc_get_order($order_id);
     325            $items = $wc_order->get_items();
     326            $fiscalPositions = '';
     327            $fiscalAmount = 0;
     328           
     329            foreach ($items as $item_id => $item) {
     330                $item_data = $item->get_data();
     331                $fiscalPositions .= $item_data['quantity'] . ';';
     332                $elementPrice = $item->get_product()->get_price() * 100;
     333                $fiscalPositions .= $elementPrice . ';';
     334                $fiscalPositions .= ($item_data['total_tax']) ?: 6 . ';';   // tax
     335                $fiscalPositions .= str_ireplace([';', '|'], '', $item_data['name']) . '|';
     336               
     337                $fiscalAmount += $item_data['quantity'] * $elementPrice;
     338            }
     339            if ($wc_order->get_shipping_total()) {
     340                $fiscalPositions .= '1;' . $wc_order->get_shipping_total() * 100 . ';6;Доставка|';
     341                $fiscalAmount += $wc_order->get_shipping_total() * 100;
     342            }
     343            $fiscalDiff = abs($fiscalAmount - intval($order->get_total() * 100));
     344            if ($fiscalDiff) {
     345                $fiscalPositions .= '1;' . $fiscalDiff . ';6;Скидка;14|';
     346            }
     347            $fiscalPositions = substr($fiscalPositions, 0, -1);
     348           
     349            $args = array(
     350                'body' => array(
     351                    'sector' => $this->sector,
     352                    'reference' => $order->get_id(),
     353                    'amount' => intval($order->get_total() * 100),
     354                    'fiscal_positions' => $fiscalPositions,
     355                    'description' => sprintf(__('Order #%s', 'paygine-payment_method'), ltrim($order->get_order_number(), '#')),
     356                    'email' => $order->get_billing_email(),
     357                    // 'notify_customer' => ($this->deferred) ? 1 : 0,
     358                    'currency' => $currency,
     359                    'mode' => 1,
     360                    'url' => $this->callback_url,
     361                    'signature' => $signature
     362                )
     363            );
     364           
     365            $remote_post = wp_remote_post($paygine_url . '/webapi/Register', $args);
     366            $remote_post = (isset($remote_post['body'])) ? $remote_post['body'] : $remote_post;
     367            $paygine_order_id = ($remote_post) ?: null;
     368           
     369            if(intval($paygine_order_id) == 0){
     370                $request_body = $args['body'];
     371                $request_body['email'] = ($request_body['email']) ? 'isset' : 'isnotset';
     372                $request_body['signature'] = ($request_body['signature']) ? 'isset' : 'isnotset';
     373                $this->logIt('Не удалось зарегистрировать заказ', array('paygine_order_id' => $paygine_order_id, 'request_body' => $request_body));
     374                return false;
     375            }
     376           
     377            update_post_meta($order->get_id(), "paygine_order_id", $paygine_order_id);
     378            update_post_meta($order->get_id(), "paygine_payment_method", $paygine_operation);
     379           
     380            $signature = $this->make_signature([$this->sector, $paygine_order_id]);
     381           
     382            /*if ($this->deferred) {
     383                    wp_redirect($this->get_return_url($wc_order));
     384                    exit();
     385            }*/
     386           
     387            return array(
     388                'result' => 'success',
     389                'redirect' => "{$paygine_url}/webapi/{$paygine_operation}?sector={$this->sector}&id={$paygine_order_id}&signature={$signature}"
     390            );
     391        }
     392       
     393        public function process_refund($order_id, $amount = null, $reason = '', $from_callback = false): bool
     394        {
     395            if(!$from_callback){
     396                $paygine_url = $this->paygine_get_url();
     397                $order = wc_get_order($order_id);
     398                $paygine_order_id = get_post_meta($order_id, 'paygine_order_id', true);
     399                $halva = 'custom/svkb/';
     400                $paygine_payment_method = get_post_meta($order_id, 'paygine_payment_method', true);
     401                $halva = (strpos($paygine_payment_method, 'Installment')) ? $halva : '';
     402                $amount = intval($amount * 100) ?: intval($order->get_total() * 100);
     403                $sector = $this->sector;
     404                $currency = $this->convert_currency();
     405                $signature = $this->make_signature([$sector, $paygine_order_id, $amount, $currency]);
     406               
     407                $args = array(
     408                    'body' => array(
     409                        'sector' => $sector,
     410                        'id' => $paygine_order_id,
     411                        'signature' => $signature,
     412                        'amount' => $amount,
     413                        'currency' => $currency
     414                    )
     415                );
     416                $xml = wp_remote_post($paygine_url . '/webapi/' . $halva . 'Reverse', $args);
     417                $response = $this->parse_xml($xml['body']);
     418                if (isset($response->order_state)){
     419                    if(in_array($response->type, $this->cancel_types)){
     420                        return true;
     421                    }
     422                } else {
     423                    $this->logIt('paygine: ', array($response));
     424                    return false;
     425                }
     426            }
     427            return !($this->orderWasRefund($response));
     428        }
     429       
     430        private function paygine_get_url(): string
     431        {
     432            $paygine_url = "https://test.paygine.com";
     433            if($this->testmode == "0"){
     434                $paygine_url = "https://pay.paygine.com";
     435            }
     436            return $paygine_url;
     437        }
     438       
     439        private function make_signature(array $parameters)
     440        {
     441            return base64_encode(md5(implode('', $parameters) . $this->password));
     442        }
     443       
     444        public function parse_xml(string $xml)
     445        {
     446            $xml = simplexml_load_string($xml);
     447            if(!$xml){
     448                return false;
     449            }
     450            return json_decode(json_encode($xml));
     451        }
     452       
     453        /**
     454         * Callback from payment gateway was received
     455         **/
     456        public function callback_from_gateway()
     457        {
     458            // check payment status
     459            $paygine_order_id = intval($_REQUEST["id"]);
     460            if(!$paygine_order_id){
     461                return false;
     462            }
     463           
     464            $paygine_operation_id = intval($_REQUEST["operation"]);
     465            if(!$paygine_operation_id){
     466                $order_id = intval($_REQUEST["reference"]);
     467                $order = wc_get_order($order_id);
     468                if($order){
     469                    $order->cancel_order(__("The order wasn't paid.", 'paygine-payment_method'));
     470                }
     471               
     472                wc_add_notice(__("The order wasn't paid.", 'paygine-payment_method'), 'error');
     473                $get_checkout_url = apply_filters('woocommerce_get_checkout_url', WC()->cart->get_checkout_url());
     474                wp_redirect($get_checkout_url);
     475                exit();
     476            }
     477           
     478            // check payment operation state
     479            $signature = $this->make_signature([$this->sector, $paygine_order_id, $paygine_operation_id]);
     480           
     481            $paygine_url = $this->paygine_get_url();
     482           
     483            $context = stream_context_create(array(
     484                'http' => array(
     485                    'header' => "Content-type: application/x-www-form-urlencoded\r\n",
     486                    'method' => 'POST',
     487                    'content' => http_build_query(array(
     488                        'sector' => $this->sector,
     489                        'id' => $paygine_order_id,
     490                        'operation' => $paygine_operation_id,
     491                        'signature' => $signature
     492                    )),
     493                )
     494            ));
     495           
     496            $repeat = 3;
     497           
     498            while($repeat) {
     499               
     500                $repeat--;
     501               
     502                // pause because of possible background processing in the Paygine
     503                sleep(2);
     504                $args = array(
     505                    'body' => array(
     506                        'sector' => $this->sector,
     507                        'id' => $paygine_order_id,
     508                        'operation' => $paygine_operation_id,
     509                        'signature' => $signature
     510                    )
     511                );
     512                $xml = wp_remote_post($paygine_url . '/webapi/Operation', $args)['body'];
     513               
     514                $response = $this->parse_xml($xml);
     515                if($this->orderWasRefund($response)){
     516                    return $from_callback = true;
     517                }
     518               
     519                if(!$this->orderAsPayed($response)){
     520                    continue;
     521                }
     522               
     523                wp_redirect($this->get_return_url(wc_get_order($response->reference)));
     524                exit();
     525            }
     526           
     527            $order_id = intval($response->reference);
     528            $order = wc_get_order($order_id);
     529            if($order){
     530                $order->cancel_order(__("The order wasn't paid [1]: " . $response->message . '.', 'paygine-payment_method'));
     531            }
     532           
     533            wc_add_notice(__("The order wasn't paid [1]: ", 'paygine-payment_method') . $response->message . '.', 'error');
     534            $get_checkout_url = apply_filters('woocommerce_get_checkout_url', WC()->cart->get_checkout_url());
     535            wp_redirect($get_checkout_url);
     536            exit();
     537        }
     538       
     539        /**
     540         * Payment notify from gateway was received
     541         **/
     542        public function notify_from_gateway()
     543        {
     544            global $wp_filesystem;
     545            if (empty($wp_filesystem)) {
     546                require_once(ABSPATH . '/wp-admin/includes/file.php');
     547                WP_Filesystem();
     548            }
     549            $xml = $wp_filesystem->get_contents('php://input');
     550            $response = $this->parse_xml($xml);
     551           
     552            $order_id = intval($response->reference);
     553            $order = wc_get_order($order_id);
     554            if ($this->orderWasRefund($response)){
     555                $amount_from_response = $response->amount;
     556                $this->process_refund($order_id,$amount_from_response);
     557            }
     558            $response_order_state = 'status_' . strtolower($response->order_state);
     559            $order->update_status(str_replace('wc-', '', $this->$response_order_state));
     560            die("ok");
     561        }
     562       
     563        private function orderWasRefund($response)
     564        {
     565            $order = $this->paygine_find_order($response);
     566            if(in_array($response->type, $this->cancel_types) && ($response->state == 'APPROVED')){
     567                $order->add_order_note(__('Refund completed.', 'paygine-payment_method'));
     568                $order->cancel_order((__("The order was refund.", 'paygine-payment_method')));
     569                return true;
     570            }
     571            return false;
     572        }
     573       
     574        private function paygine_find_order($response)
     575        {
     576            if (intval($response->reference) == 0){
     577                return false;
     578            }
     579            $order_id = $response->reference;
     580           
     581            $order = wc_get_order($order_id);
     582            if(!$order){
     583                return false;
     584            }
     585            return $order;
     586        }
     587       
     588        private function orderAsPayed($response)
     589        {
     590            // looking for an order
     591            $order = $this->paygine_find_order($response);
     592           
     593            // check payment state
     594            $tmp_response = json_decode(json_encode($response), true);
     595            unset($tmp_response["signature"]);
     596            unset($tmp_response["ofd_state"]);
     597            unset($tmp_response["protocol_message"]);
     598           
     599            $signature = $this->make_signature($tmp_response);
     600            if ($signature !== $response->signature) return false;
     601           
     602            $response_order_state = 'status_' . strtolower($tmp_response['order_state']);
     603            $order->update_status(str_replace('wc-', '', $this->$response_order_state));
     604            return true;
     605           
     606            /*
     607             * сохраним в мета заказа выбранный на момент оплаты режим (1 или 2 стадии)
     608             */
     609            $paygine_mode = ($this->settings['twostepsmode']) ? 2 : 1;
     610            update_post_meta($order_id, 'paygine_payment_mode', $paygine_mode);
     611           
     612            return true;
     613        }
     614       
     615        /**
     616         * @param string $message
     617         * @param array $details
     618         */
     619        public function logIt(string $message, array $details = [])
     620        {
     621            $log = fopen(ABSPATH . $this->getLogFileName(), 'a+') or die("logging trouble");
     622            $date = date("d.m.y H:i:s");
     623            $msg = $date . "\t" . $message;
     624            if ($details) {
     625                $msg .= "\n" . print_r($details, true);
     626            }
     627            $msg .= "\n\n\n";
     628            fprintf($log, chr(0xEF) . chr(0xBB) . chr(0xBF));
     629            fwrite($log, $msg);
     630            fclose($log);
     631        }
     632       
     633        public function getLogFileName()
     634        {
     635            $signature = $this->make_signature([$this->sector, $_SERVER['HTTP_HOST']]);
     636            $signature = str_ireplace('=', '', $signature);
     637            return 'paygine_log_' . $signature . '.txt';
     638        }
     639       
     640    } // class
     641   
     642    function add_paygine_gateway($methods)
     643    {
     644        $methods[] = 'woocommerce_paygine';
     645        return $methods;
     646    }
     647   
     648    add_filter('woocommerce_payment_gateways', 'add_paygine_gateway');
    451649}
  • paygine/trunk/readme.txt

    r2795037 r2889217  
    55Requires at least: 4.7
    66Requires PHP: 7.0
    7 Stable tag: 1.1.6
     7Stable tag: 1.3.0
    88License: GPLv3
    99License URI: https://www.gnu.org/licenses/gpl-3.0.html
Note: See TracChangeset for help on using the changeset viewer.