Authorization on sites through Z-Payment (OAuth 2.0)

image Hello habrasociety! This article describes the Z-Payment API protocol based on OAuth 2.0, for authorizing users on third-party sites. We admit that registering is always lazy, not to mention the fact that you have to share personal information (at least by mail), and when you need to do this only once, laziness is twofold. That is why Internet services with a large number of users offer the possibility of authorization on third-party resources through themselves and Z-Payment in this case is no longer an exception.

Introduction


Recently, the Z-Payment team implemented a server authorization mechanism based on the OAuth 2.0 protocol, which offers developers the opportunity to create authorization on third-party sites through the Z-Payment service and request personal data of authorized users with their consent. This method allows you to implement secure user authentication on a third-party site.

In order to take advantage of access to this interface, you must fulfill 3 prerequisites:
  1. Have a registration on Z-Payment
  2. Register a site
  3. The site should receive public status, i.e. published in the Z-Catalog

The main task of Z-Payment when implementing this method of authorization is to provide developers with another tool for creating independent projects using Z-Payment resources. A successful example is the Z-Change Exchange Exchange
project. The authorization process consists of 4 steps:
  1. Transition of the user for authorization on Z-Payment
  2. User permission to access personal data. The user must agree to the provision of data requested by the 3rd party (you). If the user is not authorized on Z-Payment, then he will be asked to authorize to his personal account. After successful authorization, the user will be asked to allow access to personal data
  3. Passing the code value to the site to get the access key
  4. Getting the project server access_token access key to access the Z-Payment API

Now in order and more


Given: site. The task is to authorize / register your users through Z-Payment.

Step 1

It is necessary to redirect the user for authorization to Z-Payment, you can do this by GET or POST by request to the address z-payment.ru/enter.php with the parameters:
client_id - required parameter, four-digit, unique store identifier in Z-Payment, issued when registering the site in the system.
redirect - required parameter, the address to which the user will be redirected after authorization (the domain of the specified address must correspond to the main domain in the store settings)
display - required parameter, indicates the type of display of the authorization page. Currently supports only page, which means authorization in a separate window
scope - optional parameter, this parameter is responsible for the list of requested personal data about the user. The requested data must be separated by a comma or space.
Here is a list of the possible requested data:
0 f_name Name
1 s_name Surname
2 m_name middle name
3 birth_day Date of Birth
4 group Type of certificate
five sex Floor
6 e_mail post office
7 phone Telephone number
8 country Country
nine city Town
ten balance Wallet balance

response_type is an optional parameter, it is responsible for the type of response we want to receive, it can take values: code - if we want to make requests from a third-party server (by default) or token - if we want to make requests from a client.
As said above, a GET request should look like this:

GET: z-payment.ru/enter.php?client_id=ID_ВАШЕГО_МАГАЗИНА&redirect=http://МОЙСАЙТ.ru/login&dispaly=page&scope=f_name,s_name,m_name,phone,city,e_mail&response_type=code


Step 2

By clicking on such a link, the user will be on a page where he will be asked to give consent to provide the third person (your site) the data that you request in the scope parameter, or first log in if he has not done so, and then give consent. Further, with the consent of the user, in automatic mode, he will be transferred to the page specified in the redirect parameter.

Step 3

Returning back, the GET method will pass the parameter code, which contains the temporary code. With it, it becomes possible to request data for which the user has consented. The parameter lifetime is 15 minutes, during this period you need to get the access key to the access_token API from the requesting site. It will look like this:

GET: REDIRECT_URI?code=a2a56603b8f57a6fa4dff77380df05206c883f011c40b72630bb5ed6f6479e52a8e
В случае возникновения ошибки строка запроса будет выглядеть так:
GET: REDIRECT_URI?error=invalid_request&error_description=Invalid+display+parameter


Step 4

At the final step, we gain access by sending a request to the address z-payment.ru/api/get_access_token.php , the request, as in step 1, can be transmitted by the GET or POST method and should contain the following parameters:
client_id - required parameter, four-digit, unique store identifier in the z-payment system, issued when registering a store in the Z-Payment merchant (see above)
code - required parameter, time code received after authorization in step 3 (see above)
format_answer - optional, response format, by The default is json. It can take values: json, get.
client_secret - required parameter, is an electronic signature. It is formed as hash md5 from the glued string of request parameters and Merchant Key store password.
The data involved in the signature: client_id + code + MerchantKey - from this we take MD5
The result of the request should be an answer consisting of parameters:
access_token, expires_in key lifetime (in seconds), user_id wallet ZP number user_verification - information about user certification (takes value yes in case the user has a certificate), as well as a set of requested fields from the first request.

This completes the theoretical description of the authorization process, now
based on the foregoing, it is possible to make a software implementation of the protocol, I will do it on php 5.3 and it is assumed that the implemented class should be used in some kind of MVC architecture.
So, the task is to make a simple class for automating authorization through Z-Payment. I want to immediately make a reservation on the code:
  1. define constants need to be moved to the configuration file, I use them in the example for clarity,
  2. I do not give an example of a class that implements the IUserStorage interface, data can be stored wherever you want - a session (SessionUserStorage), a database (DbUserStorage) or a file (FileUserStorage), and therefore it is enough to know only the interface. The code is documented, so I think the additional commentary is boring.

PHP code itself
/** @desc ID магазина для которого реализуем подключение */
define("SHOP_ID", "0001");
/** @desc MERCHANT KEY указывается пользователем в настройках магазина */
define("MERCHANT_KEY", "password");

namespace Example;

/** @desc интерфейс для класса выступающего в роли хранилища данных о пользователе */ 
interface IUserStorage
{
	/** @desc загружаем данные из хранилища */
    public function load();
	/** @desc созхраняем данные о пользователе в хранилище */
    public function save(array $data);
	/** @desc очищаем данные о пользователе */
    public function erase();
}

/** @desc собственно класс реализующий логику */
class UserManager
{
    const TOKEN_URL = "https://z-payment.ru/api/get_access_token.php";
    /**
     * @var \Example\IUserStorage
     * @desc хранилище
     */
    protected $storage;
    /**
     * @var $userInfo array
     * @desc Загружаемая информация о пользователе
     */
    protected $userInfo;
    /**
     * @var \Example\Controller\Base
	 * @desc предполагаю использование класса в mvc конструкции, поэтому храним ссылку на контроллер
     */
    protected $controller;
    /**
     * @param IUserStorage $storage
     * @desc ограничиваем параметр $storage с низу интерфейсом \Example\IUserStorage
     */
    public function __construct(\Example\IUserStorage $storage)
    {
        $this->storage = $storage;
        $this->userInfo = $this->storage->load();
    }

	/** @desc получааем либо поле, либо все поля целиком */
    public function getUserInfo($field = null)
    {
        if (null === $field)
            return $this->userInfo;

        if (isset($this->userInfo[$field]))
            return $this->userInfo[$field];

        return null;
    }

    /**
     * @return bool
     * @desc проверяем авторизован ли пользователь
     */
    public function isLogin()
    {
        return ($this->userInfo != null);
    }

    /**
     * @desc текущий ZP аккаунт
     * @return string | null
     */
    public function getAccount()
    {
        if (!$this->isLogin())
            return null;
        return $this->userInfo['user_id'];
    }

    /**
     * @return bool
     * @desc проверяем есть ли у пользователя аттестат
     */
    public function isCertified()
    {
        return ($this->getUserInfo('user_verification') == 'yes');
    }

    /**
     * @return bool
     * @desc метод реализующий авторизацию
     */
    public function Login()
    {
		/** @desc получаем переданный параметр code, если mvc конструкция не используется, то можно это сделать так $code = $_REQUEST['code']; */
        $code = $this->controller->getRequest()->get("code");
        /**
         * @desc Контрольная подпись, формируется по алгоритму md5 из строки параметров запроса
         * 1+2+Merchant Key, где Merchant Key Секретный ключ магазина
         */
        $client_secret = md5(SHOP_ID . $code . MERCHANT_KEY);
		/** @desc формируем данные для запроса */
        $params = array(
			/** id магазина */
            'client_id=' . urlencode(SHOP_ID),
			/** переданный код из первого запроса */
            'code=' . urlencode($code),
			/** тип передаваемых данных */
            'format_answer=' . 'json',
			/** контрольная подпись */
            'client_secret=' . urlencode($client_secret)
        );

        $curl = curl_init(self::TOKEN_URL);
		curl_setopt($curl, CURLOPT_HEADER, 1);
		curl_setopt($curl, CURLOPT_POST, 1);
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($curl, CURLOPT_POSTFIELDS, implode("&", $params));
		/** @desc физически запрашиваем данные */
		$result = curl_exec($curl);
		curl_close($curl);
		/** @desc конвертируем в массив */
        $result = json_decode($result);
		
		/** @desc проверяем на наличие ошибки */
        if ($result->error != null)
            return false;

		/** @desc сохраняем в хранилище */
        $this->storage->save((array)$result);
        return true;
    }

    /**
     * @desc выход пользователя
     * @return void
     */
    public function LogOut()
    {
		/** @desc очищаем хранилище */
        $this->storage->erase();
    }


    /**
     * @param \Example\Controller\Base $controller
     * @return void
     * @desc настраиваем окружение приложения.
     */
    public function setController(\Example\Controller\Base $controller)
    {
	    $this->controller = $controller;
        /**
         * @var isLogin bool
         * @desc устанавливаем флаг для отображения
         */
        $controller->getView()->isLogin = $this->isLogin();
        /** @var $client_id int */
        $client_id = SHOP_ID;
        /**
         * @var $redirect_uri string
         * @desc URL возврата
         */
        $redirect_uri =  "http://example_site_domain.org/login";
        $display = "page";
        /**
         * @var $scope strung
         * @desc запрашиваемые даные
         */
        $scope = "f_name,s_name,m_name,sex,birth_day,group,e_mail,phone,country,city,balance";
        if ($this->isLogin()) {
            /** @desc если пользователь авторизован, создаём для отобржения имя фамилия */
            $controller->getView()->userName = $this->userInfo['f_name'] . " " . 
                    $this->userInfo['s_name'];
        } else {
            /** @desc если пользователь не авторизован, создаём для отображения ссылку для регистрации */
            $controller->getView()->linkZpAuth = sprintf("https://z-payment.ru/enter.php?client_id=%s&redirect_uri=%s&display=%s&scope=%s",
                $client_id, $redirect_uri, $display, $scope);
        }
    }
}


As you can see, the authorization process is almost identical to other Internet services offering this feature, especially if you look at the VK API. :)
Thank you for your patience in the process of reading my modest work and I want to answer a question that began to torment you after reading the first paragraph - “Why do I need to screw authorization through ZP if they have significantly fewer users than VK, Gmail, Facebook, etc. ? ”, I answer: Unlike the numerous OAuth protocol implementations on other sites, authorization through the Z-Payment API implies obtaining verified user information, which is very important for all projects related to e-commerce.

Read more about the Z-Payment API in the documentation on InterfaceHTTPS.zip doc file