Прием платежей

#1. Прием платежей

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

Реализовать это можно с помощью модуля "Payments".

Он может быть интегрирован с любой системой он-лайн платежей: PayPal, ЮKassa, банковскими платежными системами и т.п.
Для создания платежа, в контроллере Вашего расширения после формирования заказа добавте такой код:

$amount = 500; // Сумма платежа. Копейки передаются в виде дробной части.
$area = 'orderType'; // тип платежа (заказа или услуги)
$options = [
    // Необязательно. При оплате периодических услуг, если период не хранится на стороне Вашего расширения,
    // срок истечения оплачиваемой услуги можно хранить в этом поле.
    'time' => $months * 30 * 24 * 60 * 60,
    
    // Необязательно. Описание или назначение платежа.
    'desc' => 'Order description',
    
    // Необязательно. Id заказа на стороне разрабатываемого расширения или ID пользователя.
    // То что необходимо для идентификации платежа Вашим расширением.
    'code' => $orderId,
    
    // Необязательно. Куда перенаправить пользователя после успешной оплаты.
    // Если не указано, то пользователь будет перенаправлен к истории своих платежей
    // https://domain.tld/payments?m=balance&n=history
    'redirect' => cot_url('extension-code', ['m' => 'orders', 'id' => $orderId], '', true)
]

cot_payments_create_order($area, $amount, $options);

Пара значений $area, $options['code'] должна однозначно идентифицировать платеж, чтобы ваше расширение после оплаты могло правильно автоматически активировать оплаченную услугу.

Функция cot_payments_create_order() создает платеж и перенаправит пользователя на страницу оплаты, где пользователь сможет выбрать одну из установленных платежных систем и совершить платеж.

Payments

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

Payments

Если при создании платежа был передан параметр $options['redirect'], то в случае успешной оплаты, он будет перенаправлен по указанному адресу.

В момент когда статус платежа меняется на "Оплачен" вызывается хук payments.payment.success.
Ваше расширение должно обрабатывать этот хук для автоматической активации оплаченной услуги. Обработчику хука доступен массив с данными платежа. Это массив с данными из таблицы cot_payments, содержащий обрабатываемый платеж.

Обратите внимание, что этот хук, как правило, вызывается в момент когда платежная система вызывает web-hook передавая данные о платеже. Запрос происходит со стороны сервера платежной системы и пользователь, совершающий платеж к этому запросу отношения не имеет и сессия этого пользователя в этот момент недоступна. По этому обработчик этого хука не должен выводить никаких данных и заголовков и не совершать никаих редиректов.

Для вывода какой то информации пользователю следует использовать урл, передаваемый через $options['redirect'], если стандартного недостаточно.
Как правило, когда пользователь переходит по этому адресу, платеж уже обработан и пользователю можно отобразить результат.

Для тестирования интеграции Вашего расширения с платежным модулем можно использовать плагин Nullbilling. Это плагин с тестовой платежной системы, который никаких платежей не проводит, а имитирует успешную оплату.

#2. Подключение платежных систем

Подключение платежной системы производится установкой соотвествующего плагина. Если готового нет, то можно написать свой.
Рассмотрим создание такого плагина.

Плагин должен зарегистрировать себя как плагин платежной системы. Для этого он должен обрабатывать хук payments.billing.register и добавить свои данные в массив $cot_billings. Например так:

if (
    Cot::$cfg['plugin']['myPaymentSystem']['on']
    && !empty(Cot::$cfg['plugin']['myPaymentSystem']['apiKey'])
    && !empty(Cot::$cfg['plugin']['myPaymentSystem']['secret'])
) {
    $cot_billings['myPaymentSystem'] = [
        'plug' => 'myPaymentSystem',
        'title' => Cot::$L['tbank_title'],
        'icon' => Cot::$cfg['plugins_dir'] . '/myPaymentSystem/images/logo.png',
    ];
}

Если платежная система будет посылать уведомление (web-hook) о платеже методом POST, надо отключить для обработчика web-hook'а anti-xss защиту, иначе Cotonti не примет запрос от платежной системы. Для этого делаем обработчик хука input с таким кодом:

if (
    isset($_GET['e'])
    && $_GET['e'] === 'myPaymentSystem'
    && isset($_GET['a'])
    && $_GET['a'] === 'notify'
    && $_SERVER['REQUEST_METHOD'] === 'POST'
) {
    define('COT_NO_ANTIXSS', 1) ;
    Cot::$cfg['referercheck'] = false;
}

Поставте в проверку $_GET параметры, по которым у Вас доступен обработчик вебхука.

Плагин имеет обработчик хука standalone, но он не выводит данных пользователю, и использует его в других целях. В админке в списке расширений не нужна кнопка "Открыть" для этого плагина. Уберем ее. Для этого в обработчик хука admin.extensions.plug.list.loop добавим код:

if ($type === COT_EXT_TYPE_PLUGIN && $code === 'myPaymentSystem') {
    $t->assign([
        'ADMIN_EXTENSIONS_JUMPTO_URL' => null,
    ]);
}

И в admin.extensions.details

if ($type === COT_EXT_TYPE_PLUGIN && $code === 'myPaymentSystem') {
    $standalone = null;
    $t->assign([
        'ADMIN_EXTENSIONS_JUMPTO_URL' => $standalone,
    ]);
}

Если на странице настройки плагина в админке нужно вывести какую либо справочную информацию для администратора сайта, например по настройкам, которые нужно сделать в личном кабинете самой платежной системы, используем хук admin.config.edit.tags

if ($o === COT_EXT_TYPE_PLUGIN && $p === 'myPaymentSystem') {
    $adminHelp = '... Справка по настройке платежной системы ...';
}

И делаем обработчик хука standalone. Он решает 2 задачи, в зависимости от переданных параметров:
1) На странице выбора способа оплаты, при клике по ссылке на платежную систему, пользователь перенаправляется на эту страницу. Передается $_GET параметр pid который содержит ID платежа в таблице cot_payments.

Получим данные платежа:

// Принимаем ID платежа
$paymentId = cot_import('pid', 'G', 'INT');
if (!$paymentId) {
    cot_die_message(404);
}

// Получаем данные из БД
$payment = PaymentRepository::getById($paymentId);
if (!$payment) {
    cot_die_message(404);
}

// Проверяем что статус платежа позвляет совершить оплату.
cot_block(in_array($payment['pay_status'], PaymentDictionary::ALLOW_PAYMENT_STATUSES, true));

Дальнейший сценарий зависит от API платежной системы, но обычно он такой: отправить запрос к платежной системе для инициализации платежа на ее стороне. В ответ платежная система возвращает URL куда нужно перенаправить пользователя для совершения платежа.

Многие платежные системы позволяют передавать в параметрах инициализации платежа URL-адреса, куда нужно перенаправить пользователя после оплаты. Вы можете получить их так: URL успешного платежа - PaymentService::getSuccessUrl($paymentId)  и URL неудачного -  PaymentService::getFailUrl($paymentId)

Перед перенаправлением пользователя на сайт платежной системы для совершения платежа, переводим платеж в статус "В просессе" и указываем какая платежная система обрабатывает платеж:

PaymentService::setStatus(
    $paymentId,
    PaymentDictionary::STATUS_PROCESS,
    'myPaymentSystem',
    PaymentDictionary::METHOD_CARD,
    $psPaymentId
);

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

Некоторые платежые системы требуют каждый раз передавать уникальный ID платежа. И в ситуации когда платеж завершился неудачно и пользователь пробует заплатить еще раз, старый ID платежа уже не принимается. Для таких платежных систем Ваш плагин должен генерировать уникальный ID платежа. Например так:

$psPaymentId = $paymentId . '-' . time();

Его и сохраняем в таблице cot_payments передавая 5-й параметр в PaymentService::setStatus().

2) Когда пользователь на сайте платежной системы совершает платеж, прежде чем вернуть пользователя назад на Ваш сайт, платежная система присылает уведомление (web-hook) о статусе платежа. Нужно его обработать. Необязательно это делать в хуке standalone, можно и в хуке ajax.

Когда уведомление от платежной системы обработано и пройдены проверки в соотвествии с документацией к платежной системе, надо перевести платеж в статус "Оплачен":

PaymentService::setStatus(
    $paymentId,
    PaymentDictionary::STATUS_PAID,
    'myPaymentSystem',
    null,
    null,
    $transactionId
)

тут мы пропускаем параметры $paymentMethod, $paymentSystemPaymentId - они установлены ранее. Но передаем $transactionId (необязательно) - ID транзакции (или платежа) на стороне платежной системы. Большинство платежных систем передают его в уведомлении (web-hook'е)


Комментарии отсутствуют
Добавление комментариев доступно только зарегистрированным пользователям