Changeset 2889217
- Timestamp:
- 03/29/2023 12:43:42 PM (3 years ago)
- Location:
- paygine/trunk
- Files:
-
- 24 added
- 2 edited
-
fonts (added)
-
fonts/Montserrat-Bold.eot (added)
-
fonts/Montserrat-Bold.ttf (added)
-
fonts/Montserrat-Bold.woff (added)
-
fonts/Montserrat-Medium.eot (added)
-
fonts/Montserrat-Medium.ttf (added)
-
fonts/Montserrat-Medium.woff (added)
-
fonts/Montserrat-Regular.eot (added)
-
fonts/Montserrat-Regular.ttf (added)
-
fonts/Montserrat-Regular.woff (added)
-
fonts/Montserrat-SemiBold.eot (added)
-
fonts/Montserrat-SemiBold.ttf (added)
-
fonts/Montserrat-SemiBold.woff (added)
-
fonts/montserrat-v25-latin-regular.eot (added)
-
fonts/montserrat-v25-latin-regular.svg (added)
-
fonts/montserrat-v25-latin-regular.ttf (added)
-
fonts/montserrat-v25-latin-regular.woff (added)
-
fonts/montserrat-v25-latin-regular.woff2 (added)
-
fonts/montserrat.css (added)
-
halva.svg (added)
-
halva_logo.svg (added)
-
halva_widget.php (added)
-
js (added)
-
js/scripts.js (added)
-
paygine-payment_method.php (modified) (4 diffs)
-
readme.txt (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
paygine/trunk/paygine-payment_method.php
r2792859 r2889217 1 1 <?php 2 /* 2 declare(strict_types=1); 3 4 5 /* 3 6 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 5 8 published by the Free Software Foundation. 6 9 … … 18 21 * Plugin URI: http://paygine.ru/ 19 22 * Description: Receive payments via Visa/Mastercard easily with Paygine bank cards processing 20 * Version: 1.1.623 * Version: 2.0 21 24 * Author: Paygine 22 * Tested up to: 6.1 25 * Tested up to: 6.1.1 23 26 * License: GPL3 24 27 * … … 31 34 32 35 if (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'); 35 38 } 36 39 … … 39 42 function init_woocommerce_paygine() 40 43 { 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'); 451 649 } -
paygine/trunk/readme.txt
r2795037 r2889217 5 5 Requires at least: 4.7 6 6 Requires PHP: 7.0 7 Stable tag: 1. 1.67 Stable tag: 1.3.0 8 8 License: GPLv3 9 9 License URI: https://www.gnu.org/licenses/gpl-3.0.html
Note: See TracChangeset
for help on using the changeset viewer.