Подключение оплаты через Tinkoff Bank



  • Всем привет. Только закончил прикручивать оплату через Tinkoff Bank и пока еще не все забыл хочу поделить наработками.
    Предполагается, что у вас уже настроен Shopkeeper и остается только прикрутить оплату. В Тинькове вы тоже зарегались и получили Идентификатор терминала и Пароль.
    В репозитарии ModX есть модуль Тинькова, но он там очень сырой и без документации, в общем то его я и переписал почти полностью.

    Первым делом необходимо перейти на страницу Оформления заказа или Корзину и установить в вызове снипета FormIt параметр &redirectTo=100 равным ID страницы где вызывается снипет Тинькова. У меня эта страница называется Оплата и содержит вызов снипета [[!Tinkoff]]

    Добавим в настройки Shopkeeper`a способ оплаты Картой (card).

    payment.zip
    Качаем архив и закидываем в папку /assets/components/ там же и распакуем. Появится папка payment в которой найдите файл config.php и настройте под себя

    Содержимое файла config.php

    <?php
    define('PAY_URL', 'https://securepay.tinkoff.ru/rest/Init');// URL запроса
    define('TERMINAL_KEY', '1479721120904DEMO');// Идентификатор терминала
    define('PAY_PASSWORD', '73mq7zfg30hhhwde');	// пароль Тинькова
    define('PAY_NETMASK', '91.194.226.0/23');	// подсеть с которой приходит нотификация
    

    Содержимое файла result.php

    <?php
    	require_once '/home/u67838/foodseasons.ru/www/config.core.php'; // указать ваш абсолютный путь до файла 
    	require_once MODX_CORE_PATH.'model/modx/modx.class.php';
    	require_once MODX_CORE_PATH.'../assets/components/payment/config.php';
    
    	$ip = ip2long($_SERVER['REMOTE_ADDR']); 
    	list($net,$mask) = explode('/',PAY_NETMASK);
    	$net = ip2long($net);
    	$mask = pow(2, 32 - $mask) - 1;
    	$net = $net&~$mask;
    	if (!(($ip^$net)&~$mask)) { // Проверяем принадлежит ли IP к подсети Тинькова
    		if ($_SERVER["REQUEST_METHOD"] == "POST") {
    			if($_POST["TerminalKey"] == TERMINAL_KEY) { // Сверяем Идентификатор терминала
    				$modx = new modX();
    				$modx->initialize('web');
    				$modx->addPackage('shopkeeper3', $modx->getOption('core_path').'components/shopkeeper3/model/');
    				$order_id = $_POST['OrderId'];
    				$order = $modx->getObject('shk_order', $order_id);
    				if ( (isset($order)) && ($order > 0) ) {
    					$order_status = $order->get('status');
    					$amount = $order->get('price');
    					$amount = $amount*100; // Сумма в копейках
    					$email = $order->get('email');
    
    					$args['OrderId'] = $order_id;
    					$args['Amount'] = $amount;
    					$args['Description'] = "Оплата счета №".$order_id;
    					$args['DATA'] = "Email=".$email;
    					$args['TerminalKey'] = TERMINAL_KEY;
    					$args['Password'] = PAY_PASSWORD;
    					// генерируем наш токен, чтобы сверить с тем что пришел в нотификации
    					$token = '';
    					ksort($args);
    					foreach ($args as $arg){$token .= $arg;}
    					//echo '<p>'.$token.'</p>';
    					$token = hash('sha256', $token);
    					unset($args);
    
    					if( ($_POST["Token"] == $token) && ($_POST["ErrorCode"] == 0) && ($_POST["Success"]) ){ // Сверяем токены наш и нотификации из банка
    						$change_status = $order->set('status', 6);
    						$order->save();
    						$modx->invokeEvent('OnSHKChangeStatus',array('order_id'=>$order_id,'status'=>6));
    						echo "OK";
    					}
    				} else {
    					echo "Order not found";
    				}
    			}
    		}
    	}
    

    Далее создаем сам снипет Tinkoff

    <?php
    if ( ($_SESSION['shk_lastOrder']['payment'] != 'card') && (!$_GET['ord_id']) ){ // Проверяем что у нас способ оплаты указан card
    	$modx->sendRedirect('/checkout/success.html', 0, 'REDIRECT_HEADER'); // иначе редиректим на страницу какую хотите
    }
    
    if ( ($_GET['ord_id']) && ($_GET['payment'] == 'card') ){
    	$order_id = $_GET['ord_id'];
    }else{
    	$order_id = $_SESSION['shk_lastOrder']['id']; // получаем ID заказа
    }
    require_once MODX_BASE_PATH."assets/components/payment/config.php"; подтягиваем конфиг Тинькова
    
    $order = $modx->getObject('shk_order', $order_id);
    if ( isset($order) ){
    	$order_status = $order->get('status');
    	$amount = $order->get('price');
    	$amount = $amount*100; // Сумма в копейках
    	$email = $order->get('email');
    
    	$args['OrderId'] = $order_id?:'';
    	$args['Amount'] = $amount?:'';
    	$args['Description'] = "Оплата счета №".$order_id;
    	$args['DATA'] = "Email=".$email;
    	$args['TerminalKey'] = TERMINAL_KEY;
    	$args['Password'] = PAY_PASSWORD;
    	//token generation
    	$token = '';
    	ksort($args);
    	foreach ($args as $arg){$token .= $arg;}
    	//echo '<p>'.$token.'</p>';
    	$token = hash('sha256', $token);
    	$args['Token'] = $token;
    	unset($args['Password']);
    
    	$args = http_build_query($args);
    	//return print_r($args,true);
    	if ($curl = curl_init()){
    		curl_setopt($curl, CURLOPT_URL, PAY_URL);
    		curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
    		curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    		curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    		curl_setopt($curl, CURLOPT_POST, true);
    		curl_setopt($curl, CURLOPT_POSTFIELDS, $args);
    		$out = curl_exec($curl);
    
    		$json = json_decode($out);
    		//echo '<p>'.print_r($json,true).'</p>';
    
    		curl_close($curl);
    
    		if ($json->PaymentURL){
    		  $change_status = $order->set('status', 2);
    		  $order->save();
    		  $modx->invokeEvent('OnSHKChangeStatus',array('order_id'=>$order_id,'status'=>2));
    			$modx->sendRedirect($json->PaymentURL);
    		}
    		return $json->PaymentURL?:$out;
    	}else{
    		$modx->sendRedirect('/checkout/error.html', 0, 'REDIRECT_HEADER');
    	}
    }
    

    В личный кабинет Тинькова добавим URL для нотификации http://site.ru/assets/components/payment/result.php

    Вот собственно и вся настройка. Теперь идем в магазин, добавляем товар в корзину, выбираем способ оплаты Картой (card) и попадаем на страницу где Тиньков просит ввести данные карты, после успешной оплаты статус заказа изменится на Оплачен


  • Администраторы

    Спасибо, что поделились!



  • Действительно, спасибо.
    @Gulik, Tinkoff рекомендует логировать все входящие параметры. Считаю, что нужно обязательно добавить логирование.
    Вот пример того, как я это делаю. Код очень кривой, но работает.

    // логирование
    class Logger {
     
    	protected $fh;
     
    	public function __construct() {
    		$this->fh = fopen('core/logs/log.log', 'a+');
    	}
     
    	public function log($msg) {
    		if(!$this->fh) {
    			throw new Exception('Unable to open log file for writing');
    		}
    		if(fwrite($this->fh, $msg . "\n") === false) {
    			throw new Exception('Unable to write to log file.');
    		}
    	}
     
    	public function __destruct() {
    		fclose($this->fh);
    	}
    }
     
    $logger = new Logger();
    $logger->log(date('m-d-Y H:i:s') . ' ' . $_SERVER['REMOTE_ADDR']);
    $logger->log('$_POST: ' . print_r($_POST, true));
    $logger->log('$_GET: ' . print_r($_GET, true));
    

    Я его вставил перед проверкой токена. Предлагаю вам включить логирование в ваш снипет. Если получится, прошу вас поделиться кодом того, что получилось.



  • @Gulik, Так что на счет логирования?



  • @alexanderr Я использую просто запись нужных мне данных в файл.
    Собираю в процессе выполнения скрипта данные в переменную, а потом (в конце) пишу в файл. Но это нужно только для отладки.



Последние темы

Похоже, подключение к Форум | MODX Shopkeeper было разорвано, подождите, пока мы пытаемся восстановить соединение.