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

Уроки по PHP, Javascript и т.п.
Ответить
Alazaur
Разработчик
Разработчик
Сообщения: 1001
Зарегистрирован: 16 окт 2011, 20:25

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

Сообщение Alazaur »

Писал для себя, класс, который будет обрабатывать платежные уведомления вконтакте. Подумал, выложу сюда, может кому-то еще пригодится.
Итак, сначала сам класс, потом расскажу как использовать. Создаем файл Payments.php и суем в него этот код:

Код: Выделить всё

<?php/** * Обработчик уведомлений от платежной системы Вконтакте */class Payments {    private $_data;        /**     * @param string $secret_key секретный ключ приложения "Вконтакте"     * @param array $items двухмерный массив с имеющимися товарами.      * Внешний массив неассоциативный (или числовой), каждый элемент которого является ассоциативным массивом с обязательными ключами/параметрами:      * "item_name" - имя товара (значение item на клиентской стороне подробнее см. в документации "Вконтакте"),     * "item_title" - название товара,      * "item_price" - стоимость товара в голосах.     * И двумя необязательными     * "item_id" - идентификатор товара, если указан то будет дополнительная проверка по ID     * "item_photo" - ссылка на изображение (75x75) товара, если указан то будет отправлена вконтакту для отображения в платежном окне пользователя     */    public function __construct($secret_key, $items)     {        $this->_data = array();        $this->_data['secret_key'] = $secret_key;        $this->_data["items"] = $items;    }        /**     * Обработка пришедшего от "Вконтакте" запроса     * @param array массив $_POST пришедший в запросе от "Вконтакте"     * @param string $callProcessFunc имя функции обработчика покупки, в нее будет передан ассоциативный массив следующего вида     * array(     *      'request' => array(), //Массив, который пришел в $_POST запросе от "Вконтакте"     *      'item' => array() //Массив с параметрами покупаемого пользователем товара     * );     * Ваша функция обработчик должна возвращать идентификатор заказа в приложении или FALSE в случае ошибки     *      * @param string $callEchoFunc имя функции выводящей ответ на экран. Необязательный параметр. Если не передана, то класс будет использовать свою.     * @return boolean TRUE в случае успешного выполнения и FALSE в случае ошибки     */    public function process($request, $callProcessFunc, $callEchoFunc = array('Payments', 'echoResponse'))    {        if(!$this->testSig($request))         {            call_user_func($callEchoFunc, Payments::getError(10));            return FALSE;        }        else         {            if($request['notification_type'] == 'get_item' || $request['notification_type'] == 'get_item_test')            {                if(!$response = $this->getResponseItem($request))                {                    call_user_func($callEchoFunc, Payments::getError(20));                    return FALSE;                }                else call_user_func($callEchoFunc, $response);                return TRUE;            }            else if($request['notification_type'] == 'order_status_change' || $request['notification_type'] == 'order_status_change_test')            {                if($request['status'] != 'chargeable')                {                    call_user_func($callEchoFunc, Payments::getError(100, "Incorrect status", TRUE));                    return FALSE;                }                else if(!$item = $this->getItem($request))                {                    call_user_func($callEchoFunc, Payments::getError(20));                    return FALSE;                }                else {                    if(!$app_order_id = call_user_func($callProcessFunc, array('request'=>$request, 'item'=>$item)))                    {                        call_user_func($callEchoFunc, Payments::getError(101, "Error of call user function", TRUE));                        return FALSE;                    }                    else                     {                        call_user_func($callEchoFunc, $this->getResponse($app_order_id, $request['order_id']));                        return TRUE;                    }                }            }            else             {                call_user_func($callEchoFunc, Payments::getError(102, "Notification type is undefined", TRUE));                return FALSE;            }        }    }            /**     * Возвращает ответ об ошибке     * @param int $errorCode код ошибки     * @param string $errorMessage сообщение     * @param boolean $critical критичность TRUE/FALSE     * @return string JSON-строка с ответом об ошибке     */    public static function getError($errorCode, $errorMessage = 'User error', $critical = TRUE)    {        switch ($errorCode)        {            case 10:                $response['error'] = array('error_code'=>10,'error_msg'=>'Incorrect signature', 'critical'=>TRUE);            break;            case 20:                $response['error'] = array('error_code'=>20,'error_msg'=>'Item does not exist', 'critical'=>TRUE);            break;            default:                $response['error'] = array('error_code'=>$errorCode,'error_msg'=>$errorMessage, 'critical'=>$critical);            break;        }        return json_encode($response);    }        /**     * Дефолтная функция вывода ответа на экран     * @param string $response     */    public static function echoResponse($response)    {        echo $response;    }                /**     * Private methods     */    private function testSig($request)    {        $sig = $request['sig'];        unset($request['sig']);        ksort($request);        $str = '';        foreach ($request as $key => $value)         {          $str .= $key.'='.$value;        }        if($sig != md5($str . $this->_data['secret_key'])) return FALSE;        else return TRUE;    }        private function getItem($request)    {        for($i = 0; $i < count($this->_data['items']); $i++)        {            if($request['item'] == $this->_data['items'][$i]['item_name'])            {                if($this->_data['items'][$i]['item_id'])                {                    if($request['item_id'] == $this->_data['items'][$i]['item_id'])                    {                        return $this->_data['items'][$i];                    }                }                 else                 {                    return $this->_data['items'][$i];                }            }        }        return FALSE;    }        private function getResponseItem($request)     {        for($i = 0; $i < count($this->_data['items']); $i++)        {            if($request['item'] == $this->_data['items'][$i]['item_name'])            {                $response['response'] = array(                    'item_id'=>$this->_data['items'][$i]['item_id'],                    'title'=>$this->_data['items'][$i]['item_title'],                    'price'=>$this->_data['items'][$i]['item_price'],                    'photo_url'=>$this->_data['items'][$i]['item_photo']                );                return json_encode($response);            }        }        return FALSE;    }        private function getResponse($app_order_id, $vk_order_id)    {        $response['response'] = array(            'order_id'=>$vk_order_id,            'app_order_id'=>$app_order_id        );        return json_encode($response);    }}?>
Итак, теперь как пользоваться:
При создании экземпляра класса ему нужно передать секретный ключ приложения и массив с имеющимися товарами.
Параметры у товаров, которые будут передаваться вконтакту (звездочкой помечены обязательные):
*item_name - Уникальное имя товара, это то имя, которое из клиента передается в параметре item.
*item_title - Название товара, которое будет показываться пользователю в окне платежки.
*item_price - Цена товара в голосах.
item_photo - Иконка товара, которая будет показываться пользователю в окне платежки.
item_id - Идентификатор товара, необязателен как и иконка, если указан, то будет использоваться для дополнительной проверки.
Так же можно добавить сколь угодно других параметров для своего удобства, в работе класса они участвовать не будут, но будут переданы в функцию-обработчик.
После создания экземпляра класса, нужно вызвать метод process() с передачей в него запроса от Вконтакте и имени своей функции обработчика. В нее будет передан массив такого вида:
array(
'request' => array(); // запрос от вконтакте
'item' => array(); // покупаемый сейчас товар
);
ваша функция должна сделать что нужно (начислить пользователю монет например) и
> В случае успешного выполнения возвратить идентификатор заказа в приложении. Если конечно учет заказов не ведется, то можно просто вернуть id заказа Вконтакте.
> В случае возникновения ошибки вернуть FALSE.
Если все вышесказанное показалось сложным. То пример должен расставить все по местам:

Код: Выделить всё

<?phprequire_once("Payments.php"); //Подключаем классheader("Content-Type: text/html; charset=utf-8"); //Устанавливаем кодировку$secret_key = "fd6767hjhkdfjhf444"; //Секретный ключ приложения //Определяем массив с товарами. Разумеется, его можно не определять в скрипте, а взять из базы.$items = array(    array('item_name'=>'gold', 'item_title'=>'Один золотой', 'item_price'=>1),    array('item_name'=>'gold10', 'item_title'=>'Десять золотых', 'item_price'=>10),    array('item_name'=>'gold50', 'item_title'=>'Пятьдесят золотых', 'item_price'=>50, 'item_photo'=>'http://flapps.ru/forum/download/file.php?avatar=1985_1344258664.jpg')); //Создаем экземпляр класса$payments = new Payments($secret_key, $items);//Вызываем метод process с передачей ему $_POST запроса и имени функции-обработчика.$payments->process($_POST, 'handler'); //Функция обработчик покупкиfunction handler($data) {    /* Массив $data['request'] содержит запрос от Вконтакте, т.е. то же что и $_POST    А массив $data['item'] содержит данные о покупаемом товаре, то есть например $data['item']['item_name'] вернет имя товара.    Эта функция вызывается когда пользователь вконтакте нажал на кнопку "Оплатить", поэтому здесь можно сделать все нужное, например добавить пользователю монет.    Если все прошло успешно, то возвращаем идентификатор этого заказа в приложении. Здесь т.к. учета у меня нет то я просто возвращаю order_id вконтакте.*/    return $data['request']['order_id'];}?>
Осталось указать Вконтакте в настройках приложения путь до вашего скрипта (не до Payments.php).
Не знаю понятно ли все объяснил. Из меня учитель не особый...
Ответить