Обработчик платежных уведомлений Вконтакте.

Уроки по PHP, Javascript и т.п.

Обработчик платежных уведомлений Вконтакте.

Сообщение Alazaur » 18 янв 2013, 08:51

Писал для себя, класс, который будет обрабатывать платежные уведомления вконтакте. Подумал, выложу сюда, может кому-то еще пригодится.
Итак, сначала сам класс, потом расскажу как использовать. Создаем файл Payments.php и суем в него этот код:
  1. <?php
  2. /**
  3.  * Обработчик уведомлений от платежной системы Вконтакте
  4.  */
  5. class Payments {
  6.     private $_data;
  7.    
  8.     /**
  9.      * @param string $secret_key секретный ключ приложения "Вконтакте"
  10.      * @param array $items двухмерный массив с имеющимися товарами.
  11.      * Внешний массив неассоциативный (или числовой), каждый элемент которого является ассоциативным массивом с обязательными ключами/параметрами:
  12.      * "item_name" - имя товара (значение item на клиентской стороне подробнее см. в документации "Вконтакте"),
  13.      * "item_title" - название товара,
  14.      * "item_price" - стоимость товара в голосах.
  15.      * И двумя необязательными
  16.      * "item_id" - идентификатор товара, если указан то будет дополнительная проверка по ID
  17.      * "item_photo" - ссылка на изображение (75x75) товара, если указан то будет отправлена вконтакту для отображения в платежном окне пользователя
  18.      */
  19.     public function __construct($secret_key, $items)
  20.     {
  21.         $this->_data = array();
  22.         $this->_data['secret_key'] = $secret_key;
  23.         $this->_data["items"] = $items;
  24.     }
  25.    
  26.     /**
  27.      * Обработка пришедшего от "Вконтакте" запроса
  28.      * @param array массив $_POST пришедший в запросе от "Вконтакте"
  29.      * @param string $callProcessFunc имя функции обработчика покупки, в нее будет передан ассоциативный массив следующего вида
  30.      * array(
  31.      *      'request' => array(), //Массив, который пришел в $_POST запросе от "Вконтакте"
  32.      *      'item' => array() //Массив с параметрами покупаемого пользователем товара
  33.      * );
  34.      * Ваша функция обработчик должна возвращать идентификатор заказа в приложении или FALSE в случае ошибки
  35.      *
  36.      * @param string $callEchoFunc имя функции выводящей ответ на экран. Необязательный параметр. Если не передана, то класс будет использовать свою.
  37.      * @return boolean TRUE в случае успешного выполнения и FALSE в случае ошибки
  38.      */
  39.     public function process($request, $callProcessFunc, $callEchoFunc = array('Payments', 'echoResponse'))
  40.     {
  41.         if(!$this->testSig($request))
  42.         {
  43.             call_user_func($callEchoFunc, Payments::getError(10));
  44.             return FALSE;
  45.         }
  46.         else
  47.         {
  48.             if($request['notification_type'] == 'get_item' || $request['notification_type'] == 'get_item_test')
  49.             {
  50.                 if(!$response = $this->getResponseItem($request))
  51.                 {
  52.                     call_user_func($callEchoFunc, Payments::getError(20));
  53.                     return FALSE;
  54.                 }
  55.                 else call_user_func($callEchoFunc, $response);
  56.                 return TRUE;
  57.             }
  58.             else if($request['notification_type'] == 'order_status_change' || $request['notification_type'] == 'order_status_change_test')
  59.             {
  60.                 if($request['status'] != 'chargeable')
  61.                 {
  62.                     call_user_func($callEchoFunc, Payments::getError(100, "Incorrect status", TRUE));
  63.                     return FALSE;
  64.                 }
  65.                 else if(!$item = $this->getItem($request))
  66.                 {
  67.                     call_user_func($callEchoFunc, Payments::getError(20));
  68.                     return FALSE;
  69.                 }
  70.                 else {
  71.                     if(!$app_order_id = call_user_func($callProcessFunc, array('request'=>$request, 'item'=>$item)))
  72.                     {
  73.                         call_user_func($callEchoFunc, Payments::getError(101, "Error of call user function", TRUE));
  74.                         return FALSE;
  75.                     }
  76.                     else
  77.                     {
  78.                         call_user_func($callEchoFunc, $this->getResponse($app_order_id, $request['order_id']));
  79.                         return TRUE;
  80.                     }
  81.                 }
  82.             }
  83.             else
  84.             {
  85.                 call_user_func($callEchoFunc, Payments::getError(102, "Notification type is undefined", TRUE));
  86.                 return FALSE;
  87.             }
  88.         }
  89.     }
  90.        
  91.     /**
  92.      * Возвращает ответ об ошибке
  93.      * @param int $errorCode код ошибки
  94.      * @param string $errorMessage сообщение
  95.      * @param boolean $critical критичность TRUE/FALSE
  96.      * @return string JSON-строка с ответом об ошибке
  97.      */
  98.     public static function getError($errorCode, $errorMessage = 'User error', $critical = TRUE)
  99.     {
  100.         switch ($errorCode)
  101.         {
  102.             case 10:
  103.                 $response['error'] = array('error_code'=>10,'error_msg'=>'Incorrect signature', 'critical'=>TRUE);
  104.             break;
  105.             case 20:
  106.                 $response['error'] = array('error_code'=>20,'error_msg'=>'Item does not exist', 'critical'=>TRUE);
  107.             break;
  108.             default:
  109.                 $response['error'] = array('error_code'=>$errorCode,'error_msg'=>$errorMessage, 'critical'=>$critical);
  110.             break;
  111.         }
  112.         return json_encode($response);
  113.     }
  114.    
  115.     /**
  116.      * Дефолтная функция вывода ответа на экран
  117.      * @param string $response
  118.      */
  119.     public static function echoResponse($response)
  120.     {
  121.         echo $response;
  122.     }
  123.    
  124.    
  125.    
  126.     /**
  127.      * Private methods
  128.      */
  129.     private function testSig($request)
  130.     {
  131.         $sig = $request['sig'];
  132.         unset($request['sig']);
  133.         ksort($request);
  134.         $str = '';
  135.         foreach ($request as $key => $value)
  136.         {
  137.           $str .= $key.'='.$value;
  138.         }
  139.         if($sig != md5($str . $this->_data['secret_key'])) return FALSE;
  140.         else return TRUE;
  141.     }
  142.    
  143.     private function getItem($request)
  144.     {
  145.         for($i = 0; $i < count($this->_data['items']); $i++)
  146.         {
  147.             if($request['item'] == $this->_data['items'][$i]['item_name'])
  148.             {
  149.                 if($this->_data['items'][$i]['item_id'])
  150.                 {
  151.                     if($request['item_id'] == $this->_data['items'][$i]['item_id'])
  152.                     {
  153.                         return $this->_data['items'][$i];
  154.                     }
  155.                 }
  156.                 else
  157.                 {
  158.                     return $this->_data['items'][$i];
  159.                 }
  160.             }
  161.         }
  162.         return FALSE;
  163.     }
  164.    
  165.     private function getResponseItem($request)
  166.     {
  167.         for($i = 0; $i < count($this->_data['items']); $i++)
  168.         {
  169.             if($request['item'] == $this->_data['items'][$i]['item_name'])
  170.             {
  171.                 $response['response'] = array(
  172.                     'item_id'=>$this->_data['items'][$i]['item_id'],
  173.                     'title'=>$this->_data['items'][$i]['item_title'],
  174.                     'price'=>$this->_data['items'][$i]['item_price'],
  175.                     'photo_url'=>$this->_data['items'][$i]['item_photo']
  176.                 );
  177.                 return json_encode($response);
  178.             }
  179.         }
  180.         return FALSE;
  181.     }
  182.    
  183.     private function getResponse($app_order_id, $vk_order_id)
  184.     {
  185.         $response['response'] = array(
  186.             'order_id'=>$vk_order_id,
  187.             'app_order_id'=>$app_order_id
  188.         );
  189.         return json_encode($response);
  190.     }
  191. }
  192. ?>


Итак, теперь как пользоваться:
При создании экземпляра класса ему нужно передать секретный ключ приложения и массив с имеющимися товарами.
Параметры у товаров, которые будут передаваться вконтакту (звездочкой помечены обязательные):
*item_name - Уникальное имя товара, это то имя, которое из клиента передается в параметре item.
*item_title - Название товара, которое будет показываться пользователю в окне платежки.
*item_price - Цена товара в голосах.
item_photo - Иконка товара, которая будет показываться пользователю в окне платежки.
item_id - Идентификатор товара, необязателен как и иконка, если указан, то будет использоваться для дополнительной проверки.
Так же можно добавить сколь угодно других параметров для своего удобства, в работе класса они участвовать не будут, но будут переданы в функцию-обработчик.
После создания экземпляра класса, нужно вызвать метод process() с передачей в него запроса от Вконтакте и имени своей функции обработчика. В нее будет передан массив такого вида:
array(
'request' => array(); // запрос от вконтакте
'item' => array(); // покупаемый сейчас товар
);

ваша функция должна сделать что нужно (начислить пользователю монет например) и
> В случае успешного выполнения возвратить идентификатор заказа в приложении. Если конечно учет заказов не ведется, то можно просто вернуть id заказа Вконтакте.
> В случае возникновения ошибки вернуть FALSE.
Если все вышесказанное показалось сложным. То пример должен расставить все по местам:
  1. <?php
  2. require_once("Payments.php"); //Подключаем класс
  3. header("Content-Type: text/html; charset=utf-8"); //Устанавливаем кодировку
  4. $secret_key = "fd6767hjhkdfjhf444"; //Секретный ключ приложения
  5.  
  6. //Определяем массив с товарами. Разумеется, его можно не определять в скрипте, а взять из базы.
  7. $items = array(
  8.     array('item_name'=>'gold', 'item_title'=>'Один золотой', 'item_price'=>1),
  9.     array('item_name'=>'gold10', 'item_title'=>'Десять золотых', 'item_price'=>10),
  10.     array('item_name'=>'gold50', 'item_title'=>'Пятьдесят золотых', 'item_price'=>50, 'item_photo'=>'http://flapps.ru/forum/download/file.php?avatar=1985_1344258664.jpg')
  11. );
  12.  
  13. //Создаем экземпляр класса
  14. $payments = new Payments($secret_key, $items);
  15. //Вызываем метод process с передачей ему $_POST запроса и имени функции-обработчика.
  16. $payments->process($_POST, 'handler');
  17.  
  18. //Функция обработчик покупки
  19. function handler($data)
  20. {
  21.     /* Массив $data['request'] содержит запрос от Вконтакте, т.е. то же что и $_POST
  22.     А массив $data['item'] содержит данные о покупаемом товаре, то есть например $data['item']['item_name'] вернет имя товара.
  23.     Эта функция вызывается когда пользователь вконтакте нажал на кнопку "Оплатить", поэтому здесь можно сделать все нужное, например добавить пользователю монет.
  24.     Если все прошло успешно, то возвращаем идентификатор этого заказа в приложении. Здесь т.к. учета у меня нет то я просто возвращаю order_id вконтакте.*/
  25.     return $data['request']['order_id'];
  26. }
  27. ?>

Осталось указать Вконтакте в настройках приложения путь до вашего скрипта (не до Payments.php).
Не знаю понятно ли все объяснил. Из меня учитель не особый...

За это сообщение автора Alazaur поблагодарили - 2:
laggerlymanu, Влад
Alazaur
Разработчик
Разработчик
 
Автор темы
Сообщения: 1001
Зарегистрирован: 16 окт 2011, 20:25
Благодарил (а): 24 раз.
Поблагодарили: 121 раз.

Чтобы убрать блок с рекламой, зарегистрируйтесь на форуме или войдите.

Google
 



Вернуться в Уроки на другие темы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 0