Документация InstantCMS

для администраторов и разработчиков

Инструменты пользователя

Инструменты сайта


manual:components:billing:api:payments

Биллинг: Подключение платежной системы

В биллинге используется модульная система для интеграции с платежными системами. Для того чтобы подключить новую систему необходимо написать модуль для нее.

Выбор системного имени

Каждая платежная система в биллинге имеет системное имя, которое может состоять из одного латинского слова и цифр. Системное имя используется в именах файлов и классов.

В качестве примера, мы будем считать что наша новая платежная система называется «New Payment System», а ее системное имя – newpayment. Здесь и далее newpament нужно будет заменять на системное имя вашей платежной системы.

Файлы и папки

Модули платежных систем хранятся в папке system/controllers/billing/systems. Внутри этой папки необходимо создать папку для новой платежной системы. Название папки совпадает с системным именем.

Например, system/controllers/billing/systems/newpayment.

В папке системы нужно создать два PHP-файла:

options.form.php Файл, содержащий описание формы настроек платежной системы.
newpayment.php Основной файл, внутри которого будет находиться класс модуля для данной платежной системы. Название файла совпадает с системным именем.

Языковой файл

Платежная система может иметь языковой файл, в котором находятся необходимые тексты (например, названия опций).

Языковой файл системы должен иметь адрес: languages/язык/controllers/billing/systems/newpayment.php.

Названия констант в языковом файле должны начинаться с префикса LANG_BILLING_SYSTEM_NEWPAYMENT_.

Пример языкового файла:

<?php
	define('LANG_BILLING_SYSTEM_NEWPAYMENT_SHOP', 'Идентификатор магазина');
	define('LANG_BILLING_SYSTEM_NEWPAYMENT_KEY', 'Ключ цифровой подписи');

Настройки платежной системы

В файле system/controllers/billing/systems/newpayment/options.form.php нужно создать стандартную форму с полями для опций. Эти поля будут выводиться в форме настроек платежной системы в админке компонента.

Класс формы должен называться в формате form{системное-имя-с-большой-букввы}SystemOptions.

Для нашего примера это formNewpaymentSystemOptions

Имена полей формы должны начинаться с префикса options:.

Пример файла формы настроек:

<?php
class formNewpaymentSystemOptions extends cmsForm{    
    public function init(){    
        return array(                    
            'options' => array(
                'type' => 'fieldset',
                'childs' => array(
 
                    new fieldString('options:shop_id', array(
                        'title' => LANG_BILLING_SYSTEM_NEWPAYMENT_SHOP,
                        'rules' => array(
                            array('required')
                        )
                    )),
 
                    new fieldString('options:key', array(
                        'title' => LANG_BILLING_SYSTEM_NEWPAYMENT_KEY,
                        'is_password' => true,
                        'rules' => array(
                            array('required')
                        )                        
                    )),
 
                )
            )            
        );        
    }
}

Класс платежной системы

В файле system/controllers/billing/systems/newpayment/newpayment.php должен быть определен класс платежной системы.

Класс должен называться в формате system{системное-имя-с-большой-букввы} и наследоваться от класса billingPaymentSystem.

Например:

class systemNewpayment extends billingPaymentSystem { }

Внутри класса могут быть определены несколько методов. Ниже подробно рассмотрим каждый из них.

Описание параметров методов

Многие методы используют одни и те же входящие параметры, поэтому опишем их сразу:

Параметр Значение
$order Массив с информацией о заказе на пополнение баланса. Содержит поля: id - номер заказа, summ - сумма заказа в реальной валюте, amount - сумма заказа во внутренней валюте, description - описание заказа
$options Массив с настройками платежной системы
$rate Курс валюты платежной системы, по отношению к реальной валюте сайта. С его помощью можно получить сумму, выставляемую к оплате в данной платежной системе, по формуле: $order['summ'] * $rate
$request Объект типа cmsRequest, содержащий информацию о текущем запросе (GET/POST). Получить переменную из запроса можно с помощью метода:
$request->get('имя переменной')
$model Модель компонента биллинг. Используется для вызова методов, изменяющих статус заказа

Получение списка полей для платежной формы

public function getPaymentFormFields($order, $options, $rate=1)

Обычно, проведение платежа в любой системе начинается с отправки платежной формы на специальный URL, предоставленный системой. Этот URL указывается в настройках системы в админке биллинга. Форма же генерируется динамически.

Когда пользователь пополняет баланс, он сначала выбирает количество валюты для покупки и нажимает «Продолжить». После этого он попадает на страницу «Проверьте правильность заказа». На самом деле эта страница нужна потому, что именно на ней выводится платежная форма. И нажатие кнопки «Перейти к оплате» отправляет платежную форму на нужный URL.

Метод getPaymentFormFields() возвращает набор полей, которые будут добавлены в платежную форму. Этот набор уникален для каждой системы. Набор возвращается в виде массива, в котором ключи это названия полей, а элементы – значения.

Например, если наша платежная система требует наличия в форме полей shop_id и summ, то метод может выглядеть так:

public function getPaymentFormFields($order, $options, $rate=1){
 
    // вычисляем сумму к оплате, по курсу    
    $summ = number_format(round($order['summ'] * $rate, 2), 2, '.', '');
 
    // получаем id магазина из настроек
    $shop_id = $options['shop_id'];
 
    return array(
        'shop_id' => $shop_id,
        'summ' => $summ
    );
 
}

Кроме того, в платежную форму можно добавить интерактивные поля, значения которых должен будет заполнить пользователь. В этом случае, в качестве значения поля в возвращаемом массиве используется экземпляр класса поля. Например:

return array(
    'summ' => $summ,
    'order_id' => $order['id'], 
    'comment' => $order['description'], 
    'phone' => new fieldString('phone', array(
        'title' => 'Номер телефона для выставления счета',
        'hint' => 'В формате <strong>+79221234567</strong>',
    ))
);

Дополнительная обработка данных после платежной формы

public function preparePayment($request, $model, $options, $rate=1)

Некоторые платежные системы используют более сложные механизмы для запуска платежа, чем простая отправка формы. Поэтому предусмотрена возможность отдать модулю платежной системы полный контроль над отправкой запроса о платеже.

Схема реализуется в три этапа:

  1. В качестве платежного URL в настройках системы указывается: billing/prepare/{системное-имя}, например billing/prepare/newpayment;
  2. Платежная форма по-прежнему формируется, но отправляется уже не в систему, а на внутренний URL, указанный выше;
  3. По данному URL срабатывает метод preparePayment() в нашем классе, и этот метод самостоятельно производит общение с реальной платежной системой (например, с помощью CURL) и редиректит куда требуется.

Такая схема используется в случаях, когда:

  • Запрос о платеже нужно послать на несколько адресов одновременно (чего нельзя добиться с помощью простой формы);
  • Для проведения платежа необходима дополнительная информация от пользователя (например, Qiwi требует ввода номера телефона. Поэтому в платежной форме этот номер запрашивается и затем отправляется на внутренний URL, где уже происходит обработка и отправка всех данных в Qiwi).

Примеры реализации описанного подхода вы можете посмотреть в коде модулей для систем Test и Qiwi из стандартной поставки биллинга.

Переопределение платежного URL

public function getPaymentURL($options)

Некоторые платежные системы используют динамический URL для отправки платежа. Тогда его не получится просто указать в настройках, т.к. при каждом платеже он будет разным.

Специально на этот случай в классе может быть описан метод getPaymentURL(), который возвращает URL для отправки платежной формы. Если этого метода нет - используется URL из настроек платежной системы.

Пример метода из модуля для системы OnPay:

public function getPaymentURL($options){
    return "https://secure.onpay.ru/pay/{$options['merchant_login']}";	
}

Получение оповещения о платеже

public function processPayment($request, $model, $options, $rate=1)

Большинство платежных систем умеют сообщать об оплате заказа путем вызова определенного URL на сайте магазина. Чаще всего в настройках мерчантов он называется как Result URL или Status URL. Вызов этого URL происходит в фоновом режиме, незаметно для пользователя и без использования его браузера.

В биллинге данный URL уникален для каждой платежной системы. Он имеет вид: http://example.com/billing/process/newpayment, где example.com – адрес сайта, newpayment – системное имя платежной системы.

При вызове данного URL в классе модуля системы запускается метод processPayment().

Внутри метода обычно реализуется следующий алгоритм:

  1. Получаем данные, переданные платежной системой (номер заказа, сумма, статус оплаты и т.п.);
  2. По номеру заказа получаем информацию о заказе из базы;
  3. Сверяем правильность данных (что сумма заказа не изменена, цифровая подпись совпадает и т.п.);
  4. Если все верно – отмечаем заказ как оплаченный (на внутренний баланс пользователя при этом зачисляется купленная им внутренняя валюта);
  5. Возвращаем ответ в формате, требуемом платежной системой.

Данные, передаваемые на Result URL каждой платежной системой уникальны. Их описание нужно смотреть в документации по API платежной системы. Получение данных из запроса (GET/POST) происходит с помощью объекта $request, например:

$order_id = $request->get('order_id');

Получение заказа по известному ID осуществляется с помощью модели:

$order = $model->getOperation( $order_id );

Смена статуса заказа на «оплаченный» происходит также с помощью модели:

$model->acceptPayment( $order_id );

Пример реализации метода для системы Robokassa:

public function processPayment($request, $model, $options, $rate=1){
 
    // Получаем данные, переданные платежной системой
    // на наш Result URL
    $op_id = $request->get('InvId'); // - id заказа
    $out_summ = $request->get('OutSum'); // - сумма 
    $out_signature = $request->get('SignatureValue'); // - подпись
 
    // Проверяем что получен id заказа
    if (!$op_id) { echo LANG_BILLING_ERR_ORDER_ID; return false; }
 
    // Получаем заказ из базы по id
    $operation = $model->getOperation($op_id);
 
    // Проверяем что такой заказ найден
    if (!$operation) { echo LANG_BILLING_ERR_ORDER_ID; return false; }        
 
    // Проверяем что этот заказ еще не оплачен
    if ($operation['status'] != billing::STATUS_CREATED) { echo LANG_BILLING_ERR_ORDER_ID; return false; }
 
    // Вычисляем сумму к оплате, по курсу платежной
    // системы к реальной валюте нашего сайта
    $summ = round($operation['summ'] * $rate, 2);
 
    // Если требуемая сумма не совпала с суммой, 
    // оплаченной пользователем, значит произошла подмена или ошибка
    if ($summ != $out_summ) {  echo LANG_BILLING_ERR_SUMM; return false; }
 
    // Вычисляем контрольную подпись по алгоритму,
    // описанному в документации платежной системы Robokassa
    $sig = array($out_summ, $op_id, strrev($options['password1']));
    $sig = mb_strtoupper(md5(implode(':', $sig)));
 
    // Проверяем что подписи совпадают
    if ($sig != $out_signature) { echo LANG_BILLING_ERR_SIG; return false; }
 
    // Принимаем платеж как успешно завершенный
    // Баланс пользователя при этом пополняется
    $model->acceptPayment($op_id);        
 
    return true;
 
}

Получение оповещения об успехе платежа

После успешного проведения платежа обычно платежные системы переадресуют пользователя на специальный URL магазина, по которому расположена страница с текстом типа «Оплата успешно принята». Обычно такой URL называется в мерчантах как Success URL.

В биллинге данный URL уникален для каждой платежной системы. Он имеет вид: http://example.com/billing/success/newpayment, где example.com – адрес сайта, newpayment – системное имя платежной системы.

Чаще всего, помимо простой переадресации, платежные системы также высылают на этот адрес ID оплаченного заказа.

В реальной жизни иногда возможны ситуации, когда переадресация на Success URL происходит раньше, чем вызов Result URL. То есть когда платежная система уже знает что платеж прошел и отправляет пользователя обратно на сайт, но при этом самому сайту сообщение о платеже еще не пришло.

Поэтому, для избежания неприятностей, на Success URL мы должны удостовериться что заказ оплачен и баланс пользователя пополнен. И если это не так, то заставить пользователя ждать пока сайт не получит уведомление о проведенном платеже.

Поскольку номер заказа приходит на Success URL вместе с пользователем, в модуле платежной системы должен быть определен метод:

public function getSuccessOrderId($request)

Этот метод должен получать из объекта $request значение ID заказа, передаваемое платежной системой на Success URL, и возвращать его. Используя полученный ID биллинг найдет заказ и проверит его статус. Если заказ окажется по-прежнему не оплаченным, биллинг будет проверять его статус каждые несколько секунд, до тех пор пока уведомление об оплате не поступит. Пользователь при этом будет видеть просьбу подождать.

Пример метода:

public function getSuccessOrderId($request){        
    return $request->get('InvId');        
}

Получение оповещения о неудаче платежа

После неудачи в проведении платежа обычно платежные системы переадресуют пользователя на специальный URL магазина, по которому расположена страница с текстом типа «Ошибка оплаты». Обычно такой URL называется в мерчантах как Fail URL.

В биллинге данный URL един для всех платежных систем. Он имеет вид: http://example.com/billing/fail, где example.com – адрес сайта.

Никаких дополнительных действий в модулей платежной системы для обработки неудачных платежей не требуется.

Примеры подключений

С примерами реализаций модулей для несколько платежных систем вы можете ознакомиться в папке system/controllers/billing/systems из комплекта поставки биллинга.

manual/components/billing/api/payments.txt · Последнее изменение: 27.05.2020 17:48 — admin