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