Быстрый старт

Введение

Эта инструкция — о том, как организовать приём платежей через Payment Page. С тем, чтобы платёжная форма вызывалась из веб-сервиса и возвращала пользователей к нему. И чтобы при этом можно было использовать проверенные быстрые решения — с чёткими инструкциями, библиотеками и примерами кода И с использованием внутри программных решений на одном из трёх популярных языков программирования — PHP, Python или Java.



Если вам актуально что-то другое, можно сориентироваться в вариантах.

Рис.: Другие варианты

  • Если надо научиться делать платёжные ссылки для открытия Payment Page, можно разобраться с тем, как работать с ними вручную через Dashboard и автоматически через Gate.
  • Если надо основательно разобраться со схемой и возможностями работы с Payment Page, можно пройти сюда.
  • Если актуально что-то ещё, можно обратиться к другим разделам (например, начав здесь) и к специалистам ecommpay.

На этом с вводными всё. Можно переходить к делу.

Краткая теория

Проекты и ключи

Работу с платёжной платформой ecommpay можно сравнить с использованием услуг гостиницы. Так, для заселения в гостиницу обычно необходимо получить номер и ключ от него, а для начала работы с платформой надо получить... проект и ключ от него. И как с номерами в гостиницах, в платформе может предоставляться разное количество проектов для одного клиента — под разные цели и задачи — при этом для каждого проекта необходим свой ключ.

Как правило, для работы достаточно одного тестового и одного рабочего проектов. Это типичный случай, и в рамках быстрого старта мы исходим из него. Если же вам по какой-либо причине необходимо больше проектов, это стоит обсудить с курирующим менеджером, но начать всё также можно с одного тестового проекта.

Для начала работы с платформой надо получить проект и ключ от него. Если у вас уже есть идентификатор тестового проекта (project_id) и секретный ключ для него (secret_key), можно приготовиться к их использованию и переходить дальше. Если же вы ещё не бронировали тестовый проект, самое время это сделать и вернуться сюда.

Схема работы

Чтобы корректно вызывать Payment Page, надо настроить сбор параметров, их подписывание и вызов формы. При этом подписывание данных (для которого необходим секретный ключ) важно выполнять в серверной части веб-сервиса, а вызов формы — в клиентской. Также для оперативного контроля результатов полезно настроить в серверной части приём оповещений от платёжной платформы.

В целом это выглядит так.

  В клиентской части веб‑сервиса В серверной части веб‑сервиса В платёжной платформе
1 Формируем заказ. Фиксируем параметры платежа и передаём их в серверную часть (чтобы подписать)
2 Дополняем (если это актуально) и подписываем параметры платежа, после чего передаём информацию в клиентскую часть
3 Формируем и отправляем в платёжную платформу запрос на открытие Payment Page
4 Принимаем запрос, готовим и открываем форму пользователю, обрабатываем его действия и проводим платёж, после чего отправляем оповещение о результате платежа и возвращаем пользователя к веб-сервису
5 Принимаем оповещение о результате платежа и обновляем статус заказа
6 Отображаем пользователю информацию об оплате заказа и дальнейших действиях (если они необходимы, например для доставки товара)

Реализовывать эту схему в клиентской и серверной частях веб-сервиса в общем случае можно самыми разными способами. Здесь, в рамках быстрого старта, для удобства и скорости запуска все необходимые процедуры описываются с максимальным использованием уже готовых компонентов (таких как SDK) и примеров кода. Но вы всегда можете варьировать наши готовые компоненты со своими решениями.

Параметры вызова формы

Чтобы открыть пользователю платёжную форму, в простейшем случае достаточно определиться с суммой и валютой платежа и добавить к этим двум параметрам три идентификатора: проекта, платежа и пользователя. Таким образом, обязательных параметров всего пять. (И технически к ним ещё обязательна подпись.)

Табл. 1. Обязательные параметры
Параметр Описание

project_id
integer

Идентификатор проекта. Его вместе с ключом выдаёт ecommpay и его важно точно указывать даже в тестовых запросах. Иначе… стоит ждать реакцию, как при попытке зайти в чужой гостиничный номер, от платёжной платформы.
Пример: 57123

payment_id
string

Идентификатор платежа. Он может быть произвольным, но каждый раз должен быть уникальным в рамках используемого проекта. Иначе стоит ждать ошибку вызова, от веб-сервиса.
Пример: payment_443

payment_amount
integer

Сумма платежа. В тестовых запросах может быть произвольной, а в реальных должна точно соответствовать сумме заказа. Приводится, в дробных единицах валюты.
Пример: 1815 (для суммы 18,15)

payment_currency
string

Код валюты платежа. Приводится, в трёхбуквенном формате ISO 4217 alpha-3. В тестовых запросах могут использоваться любые из действующих кодов, а в реальных каждый раз должен использоваться код той валюты, в которой инициируется платёж. Для сверки можно использовать справочник валют.
Пример: EUR

customer_id
string

Идентификатор пользователя. Может быть произвольным и повторяемым в разных запросах, но для каждого реального пользователя должен быть однозначно сопоставляемым с его учётной записью в веб-сервисе и уникальным в рамках проекта. Иначе возможны различные коллизии, в том числе с отображением сохранённых данных платёжных инструментов одного пользователя другому.
Пример: customer_112

Как именно собирать эти параметры (и в том числе, какие из них задавать в клиентской части, а какие в серверной) — решать вам (с оглядкой на архитектуру вашего веб-сервиса и иные факторы). При этом для первых тестовых вызовов сбор параметров можно не автоматизировать вовсе, если что, этим можно заняться и после первичного тестирования работы с формой. Здесь же остаётся сказать, что в дополнение к обязательным параметрам можно использовать и другие, для управления видом и поведением платёжной формы, но поскольку с этими параметрами лучше разбираться после того, как настроен базовый вызов Payment Page, предметный разговор о них дальше.

Базовая реализация

Варианты работы

Реализовывать функции веб-сервиса для работы с платёжной формой и оповещениями можно по-разному, в том числе за счёт создания своих программных решений и за счёт применения CMS-модулей. В рамках быстрого старта мы рассматриваем два варианта реализации функций веб-сервиса для работы с платёжной формой и оповещениями:

  • с применением в серверной части веб-сервиса SDK от ecommpay;
  • с использованием в серверной части веб-сервиса готового кода от ecommpay

Различия между этими вариантами можно считать вкусовыми. С SDK может быть чуть проще, а также доступнее при работе с другими языками программирования (полный набор материалов о работе с SDK представлен в отдельном разделе). С примерами кода же может быть чуть прозрачнее и гибче в плане встраивания в свои решения (в том числе при работе с подписыванием данных). Но оба варианта достаточно быстры и полноценны, и вы можете выбрать любой из них.

Вместе с тем, независимо от варианта реализации серверных функций, в клиентской части веб-сервиса в рамках быстрого старта мы рассматриваем разные варианты вызова платёжной формы, в том числе с использованием библиотек от ecommpay. И в целом задачи реализации сводятся к следующим.

В серверной части В клиентской части
  • подключить библиотеки (при работе с SDK)
  • обеспечить дополнение параметров (насколько актуально)
  • обеспечить подписывание данных
  • обеспечить приём оповещений
  • подключить библиотеки (если актуально)
  • обеспечить сбор параметров (насколько актуально)
  • обеспечить вызов формы

Сбор и дополнение параметров, как и было сказано в теоретическом обзоре, могут выполняться разными способами (для первичного тестирования допустимо и вручную) и остаются за вами. Остальные действия разобраны далее.

Работа с SDK

Подключаем и настраиваем.

Рис.: PHP

1 Подключаем SDK

  1. Если не делали этого ранее, загружаем, устанавливаем и настраиваем менеджер зависимостей Composer (https://getcomposer.org/).
  2. В командной строке операционной системы переходим в каталог с исходным кодом веб-сервиса и выполняем команду composer require ecommpay/paymentpage-sdk.
  3. Подключаем скрипт autoload.php в исходном коде веб-сервиса.
// Подключение библиотек
require` __DIR__.'../../vendor.autoload.php';

 

2 Обеспечиваем подписывание данных

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

// Подписывание данных и формирование ссылки

// создание объекта класса Payment и указание идентификаторов проекта и платежа
$payment = new ecommpay Payment('57123', 'payment_443');

// указание других обязательных параметров
$payment->setPaymentAmount(1815)->setPaymentCurrency('EUR'); // сумма и валюта платежа
$payment->setCustomerId('customer_112'); // идентификатор пользователя

// создание объекта класса Gate и указание секретного ключа
$gate = new ecommpay\Gate('<secret_key>');

// формирование ссылки для вызова платёжной формы
$url = $gate->getPurchasePaymentPageUrl($payment);

 

3 Настраиваем приём оповещений

Чтобы оперативно узнавать о результатах платежей и получать другую значимую информацию, следует настроить приём и обработку программных оповещений от платёжной платформы. Это делается в три шага:

  1. Определяем и задаём в платёжной платформе адрес для приёма веб-сервисом оповещений по проекту (сначала можно задать один общий адрес, а после — несколько, под разные события). Для этого открываем в интерфейсе Dashboard раздел Проекты и используем инструменты на вкладке Оповещения.
  2. Настраиваем проверку целостности и разбор оповещений, поступающих на указанный URL, с использованием SDK.
  3. Настраиваем отправку синхронных HTTP-ответов о приёме оповещений: 200 ОК если подпись корректна и 400 Bad Request если подпись некорректна.
// Работа с оповещениями

// создание объекта класса Gate и указание секретного ключа
$gate = new ecommpay\Gate('<secret_key>');

// создание объекта класса Callback и указание JSON-строки с данными
// из оповещения ($data), с проверкой целостности данных
$callback = $gate->handleCallback($data);

// использование методов для работы с оповещениями
Callback::getPaymentId();        // получение идентификатора платежа
Callback::getPaymentStatus();    // получение статуса платежа
Callback::getPayment();          // получение всей информации из оповещения

Описания используемых статусов платежей можно найти в отдельном разделе, а описание оповещений и работы с ними — в отдельной статье. При этом важно помнить, что в случаях, когда пользователь не подтверждает оплату в платёжной форме, платёж не регистрируется (и статуса по нему тоже нет).

Извлечённую из оповещений информацию можно использовать для оперативного обновления статусов заказов в веб-сервисе, информирования пользователей и других целей — в соответствии с моделью работы вашего веб-сервиса.

Рис.: Python

1 Подключаем SDK

  1. Если не делали этого ранее, загружаем, устанавливаем и настраиваем систему управления пакетами pip (https://pip.pypa.io/en/stable/).
  2. В командной строке операционной системы переходим в каталог с исходным кодом веб-сервиса и выполняем команду pip install ecommpay-sdk.
  3. Подключаем библиотеки из SDK в исходном коде веб-сервиса.
# Подключение библиотек
from payment_page_sdk.gate import Gate
from payment_page_sdk.payment import Payment

 

2 Обеспечиваем подписывание данных

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

# Подписывание данных и формирование ссылки

# создание объекта класса Payment и указание идентификаторов проекта и платежа
payment = Payment('57123', 'payment_443')

# указание других обязательных параметров
payment.payment_amount = 1815 # сумма платежа
payment.payment_currency = 'EUR' # валюта платежа
payment.customer_id = 'customer_112' # идентификатор пользователя

# создание объекта класса Gate и указание секретного ключа
gate = Gate('<secret_key>')

# формированием ссылки для вызова платёжной формы
payment_url = gate.get_purchase_payment_page_url(payment)

 

3 Настраиваем приём оповещений

Чтобы оперативно узнавать о результатах платежей и получать другую значимую информацию, следует настроить приём и обработку программных оповещений от платёжной платформы. Это делается в три шага:

  1. Определяем и задаём в платёжной платформе адрес для приёма веб-сервисом оповещений по проекту (сначала можно задать один общий адрес, а после — несколько, под разные события). Для этого открываем в интерфейсе Dashboard раздел Проекты и используем инструменты на вкладке Оповещения.
  2. Настраиваем проверку целостности и разбор оповещений, поступающих на указанный URL, с использованием SDK.
  3. Настраиваем отправку синхронных HTTP-ответов о приёме оповещений: 200 ОК если подпись корректна и 400 Bad Request если подпись некорректна.
# Работа с оповещениями

# создание объекта класса Gate и указание секретного ключа
gate = Gate('<secret_key>')

# создание объекта класса Callback и указание JSON-строки с данными
# из оповещения (data), с проверкой целостности данных
callback = gate.handle_callback(data)

# использование методов для работы с оповещениями
callback.get_payment_id()        # получение идентификатора платежа
callback.get_payment_status()    # получение статуса платежа
callback.get_payment()           # получение всей информации из оповещения

Описания используемых статусов платежей можно найти в отдельном разделе, а описание оповещений и работы с ними — в отдельной статье. При этом важно помнить, что в случаях, когда пользователь не подтверждает оплату в платёжной форме, платёж не регистрируется (и статуса по нему тоже нет).

Извлечённую из оповещений информацию можно использовать для оперативного обновления статусов заказов в веб-сервисе, информирования пользователей и других целей — в соответствии с моделью работы вашего веб-сервиса.

Рис.: Java

1 Подключаем SDK

  1. Если не делали этого ранее, загружаем SDK для Java и формируем JAR-архив из файлов, входящих в SDK.
  2. Если в каталоге проекта с исходных кодом веб-сервиса не создан каталог libs, создаём его и добавляем в него JAR-архив.
  3. Подключаем файлы из JAR-архива в исходном коде веб-сервиса.

 

2 Обеспечиваем подписывание данных

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

// Подписывание данных и формирование ссылки

// создание объекта класса Payment и указание идентификаторов проекта и платежа
Payment payment = new Payment('57123', "payment_443");

// указание других обязательных параметров
payment
    .setParam(Payment.PAYMENT_AMOUNT, 1815) // сумма платежа
    .setParam(Payment.PAYMENT_CURRENCY, "EUR") // валюта платежа
    .setParam(Payment.CUSTOMER_ID, "customer_112") // идентификатор пользователя

// создание объекта класса Gate и указание секретного ключа
Gate gate = new Gate("<secret_key>");

// получение ссылки для вызова платёжной формы
String paymentUrl = gate.getPurchasePaymentPageUrl(payment);

 

3 Настраиваем приём оповещений

Чтобы оперативно узнавать о результатах платежей и получать другую значимую информацию, следует настроить приём и обработку программных оповещений от платёжной платформы. Это делается в три шага:

  1. Определяем и задаём в платёжной платформе адрес для приёма веб-сервисом оповещений по проекту (сначала можно задать один общий адрес, а после — несколько, под разные события). Для этого открываем в интерфейсе Dashboard раздел Проекты и используем инструменты на вкладке Оповещения.
  2. Настраиваем проверку целостности и разбор оповещений, поступающих на указанный URL, с использованием SDK.
  3. Настраиваем отправку синхронных HTTP-ответов о приёме оповещений: 200 ОК если подпись корректна и 400 Bad Request если подпись некорректна.
// Работа с оповещениями

// создние объекта класса Gate и указание секретного ключа
Gate gate = new Gate("<secret_key>");

// создание объекта класса Callback и указание JSON-строки с данными
// из оповещения (data), с проверкой целостности данных
Callback callback = gate.handleCallback(data);

// использование методов для работы с оповещениями
callback.getPaymentId();        // получение идентификатор платежа
callback.getPaymentStatus();    // получение статус платежа
callback.getPayment();          // получение всей информации из оповещения

Описания используемых статусов платежей можно найти в отдельном разделе, а описание оповещений и работы с ними — в отдельной статье. При этом важно помнить, что в случаях, когда пользователь не подтверждает оплату в платёжной форме, платёж не регистрируется (и статуса по нему тоже нет).

Извлечённую из оповещений информацию можно использовать для оперативного обновления статусов заказов в веб-сервисе, информирования пользователей и других целей — в соответствии с моделью работы вашего веб-сервиса.

Работа с кодом

Встраиваем и используем.

Рис.: PHP

1 Обеспечиваем подписывание данных

// Кодирование алгоритма
class Signer
{
    const ALGORITHM = 'sha512';
    const ITEMS_DELIMITER = ';';
    const MAXIMUM_RECURSION_DEPTH = 3;
 
 
    /**
     * Generate signature
     *
     * @param array $params
     * @param string $secretKey
     * @param array $ignoreParamKeys
     * @param bool $doNotHash
     * @param bool $useUnlimitedRecursionDepth
     *
     * @return string
     */
    public static function sign(array $params, string $secretKey, array $ignoreParamKeys = [], bool $doNotHash = false, bool $useUnlimitedRecursionDepth = false): string
    {
        $paramsPrepared = self::getParamsToSign($params, $ignoreParamKeys, 1, '', $useUnlimitedRecursionDepth);
        $stringToSign = implode(self::ITEMS_DELIMITER, $paramsPrepared);
 
        return $doNotHash
            ? $stringToSign
            : base64_encode(hash_hmac(self::ALGORITHM, $stringToSign, $secretKey, true));
    }
 
    /**
     * Get parameters to sign
     *
     * @param array $params
     * @param array $ignoreParamKeys
     * @param int $currentLevel
     * @param string $prefix
     * @param bool $useUnlimitedRecursionDepth
     *
     * @return array
     */
    private static function getParamsToSign(
        array  $params,
        array  $ignoreParamKeys = [],
        int    $currentLevel = 1,
        string $prefix = '',
        bool   $useUnlimitedRecursionDepth = false
    ): array
    {
        $paramsToSign = [];
 
        foreach ($params as $key => $value) {
            if (in_array($key, $ignoreParamKeys, true)) {
                continue;
            }
 
            $paramKey = ($prefix ? $prefix . ':' : '') . $key;
 
            if (is_object($value)) {
                $value = get_object_vars($value);
            }
 
            if (is_array($value)) {
                if (!$useUnlimitedRecursionDepth && ($currentLevel >= self::MAXIMUM_RECURSION_DEPTH)) {
                    $paramsToSign[$paramKey] = (string)$paramKey . ':';
                } else {
                    $subArray = self::getParamsToSign($value, $ignoreParamKeys, $currentLevel + 1, $paramKey, $useUnlimitedRecursionDepth);
                    $paramsToSign = array_merge($paramsToSign, $subArray);
                }
            } else {
                if (is_bool($value)) {
                    $value = $value ? '1' : '0';
                } else {
                    $value = (string)$value;
                }
 
                $paramsToSign[$paramKey] = (string)$paramKey . ':' . $value;
            }
        }
 
        if ($currentLevel == 1) {
            ksort($paramsToSign, SORT_NATURAL);
        }
 
        return $paramsToSign;
    }


// Пример использования

// указание секретного ключа и параметров вызова платёжной формы
$secretKey = "jwfbfjhewbrw33383kr3js9d987";
 
$params = [
    'project_id' => 57123,     
    'payment_amount' => 1815,
    'payment_currency' => 'EUR',
    'customer_id' => 'customer_112',
    'payment_id' => 'payment_443',
];
 
// формирование подписи
$params['signature'] = Signer::sign($params, $secretKey);
 
// получение ссылки для вызова платёжной формы
$uriParams = http_build_query($params);
$link = implode('?', ['https://paymentpage.ecommpay.com/payment', $uriParams]);

 

2 Обеспечиваем приём оповещений

Чтобы оперативно узнавать о результатах платежей и получать другую значимую информацию, следует настроить приём и обработку программных оповещений от платёжной платформы. Это делается в три шага:

  1. Определяем и задаём в платёжной платформе адрес для приёма веб-сервисом оповещений по проекту (сначала можно задать один общий адрес, а после — несколько, под разные события). Для этого открываем в интерфейсе Dashboard раздел Проекты и используем инструменты на вкладке Оповещения.
  2. Настраиваем проверку целостности и разбор оповещений, поступающих на указанный URL, с использованием SDK.
  3. Настраиваем отправку синхронных HTTP-ответов о приёме оповещений: 200 ОК если подпись корректна и 400 Bad Request если подпись некорректна.
// Работа с оповещениями. Пример сверки полученной подписи с расчётной

require_once 'Signer.php';

$secretKey = '<secret_key>';

$requestBody = '{
       "project_id": 57123,
       "payment": {
           "id": "payment_443",
           "type": "purchase",
           "status": "success",
           "date": "2022-03-03T10:50:29+0000",
           "method": "card",
           "sum": {
               "amount": 1815,
               "currency": "EUR"
           },
           "description": "Test payment"
       },
       "customer": {
           "id": "customer_112",
           "ip": "127.0.0.1",
           "phone": "+60-3-xxxx-xxxx",
           "email": "jane.doe@testmail.com"
       },
       "operation": {
           "id": 15788000002076,
           "type": "sale",
           "status": "success",
           "date": "2022-03-03T10:50:29+0000",
           "sum_initial": {
               "amount": 1815,
               "currency": "EUR"
           },
           "code": "0",
           "message": "Success"
       },
       "signature": "zwoFECy7H+WZFziw9IgR9031Uu878TuIv3dhFyHvCuvzIdRAzDQA=="
   }';

$requestBodyArray = json_decode($requestBody, true);

$actualSignature = $requestBodyArray['signature'];
unset($requestBodyArray['signature']);
$expectedSignature = Signer::sign($requestBodyArray, $secretKey);

print_r('Actual signature: ' . $actualSignature . PHP_EOL);
print_r('Expected signature: ' . $expectedSignature . PHP_EOL);

if ($expectedSignature === $actualSignature) {
    print_r('Signatures are equal');
} else {
    print_r('Signatures are not equal');
}

print_r(PHP_EOL);

Описания используемых статусов платежей можно найти в отдельном разделе, а описание оповещений и работы с ними — в отдельной статье. При этом важно помнить, что в случаях, когда пользователь не подтверждает оплату в платёжной форме, платёж не регистрируется (и статуса по нему тоже нет).

Извлечённую из оповещений информацию можно использовать для оперативного обновления статусов заказов в веб-сервисе, информирования пользователей и других целей — в соответствии с моделью работы вашего веб-сервиса.

Рис.: Python

1 Обеспечиваем подписывание данных

# Кодирование алгоритма
import hmac
import base64
import hashlib
 
 
class Signer:
    def getSign(self, params, key):
        byteKey = key.encode()
        paramsPrepared = self.getParamsToSign(params)
        toSignString = ';'.join(paramsPrepared.values())
        hmacObj = hmac.new(byteKey, toSignString.encode(), hashlib.sha512)
 
return base64.b64encode(hmacObj.digest())
 
def getParamsToSign(self, params, currentLevel = 1, prefix = '', useUnlimitedRecursionDepth = False):
        paramsToSign = dict()
 
for paramKey in params.keys():
 
newParamKey = (prefix + ':' if prefix else '') + paramKey
 
if isinstance(params[paramKey], (dict)):
                if useUnlimitedRecursionDepth is False and (currentLevel >= 3):
                    paramsToSign[newParamKey] = newParamKey + ':'
                else:
                    subDict = self.getParamsToSign(params[paramKey], currentLevel + 1, newParamKey, useUnlimitedRecursionDepth)
                    paramsToSign.update(subDict)
            else:
                if isinstance(params[paramKey], (bool)):
                    value = '1' if params[paramKey] == True else '0'
                else:
                    value = str(params[paramKey])
 
paramsToSign[newParamKey] = str(newParamKey + ':' + value)
 
sortedParams = dict()
 
for key in sorted(paramsToSign.keys()):
            sortedParams[key] = paramsToSign[key]
 
return sortedParams


# Пример использования

# указание параметров вызова платёжной формы и секретного ключа
import urllib.parse
import Signer
 
params = {
    'project_id': 57123, 
    'payment_amount': 1815,
    'payment_currency': 'EUR',
    'customer_id': 'customer_112',
    'payment_id': 'payment_443',
}

# формирование подписи
signer = Signer.Signer()
 
params['signature'] = signer.getSign(params, '<secret_key>')
 
print(params['signature'])

# получение ссылки для вызова платёжной формы
jointParams = urllib.parse.urlencode(params)
 
link = '?'.join(['https://paymentpage.ecommpay.com/payment', jointParams])

 

2 Обеспечиваем приём оповещений

Чтобы оперативно узнавать о результатах платежей и получать другую значимую информацию, следует настроить приём и обработку программных оповещений от платёжной платформы. Это делается в три шага:

  1. Определяем и задаём в платёжной платформе адрес для приёма веб-сервисом оповещений по проекту (сначала можно задать один общий адрес, а после — несколько, под разные события). Для этого открываем в интерфейсе Dashboard раздел Проекты и используем инструменты на вкладке Оповещения.
  2. Настраиваем проверку целостности и разбор оповещений, поступающих на указанный URL, с использованием SDK.
  3. Настраиваем отправку синхронных HTTP-ответов о приёме оповещений: 200 ОК если подпись корректна и 400 Bad Request если подпись некорректна.
# Работа с оповещениями. Пример сверки полученной подписи с расчётной

from Signer import Signer
import base64

secretKey = '<secret_key>'
params = {
    "project_id": 57123,
    "payment": {
        "id": "payment_443",
        "type": "purchase",
        "status": "success",
        "date": "2022-03-03T10:50:29+0000",
        "method": "card",
        "sum": {
            "amount": 1815,
            "currency": "EUR"
        },
        "description": "Gagarin set"
    },
    "customer": {
        "id": "customer_112",
        "ip": "127.0.0.1",
        "phone": "+60-3-xxxx-xxxx",
        "email": "jane.doe@testmail.com"
    },
    "operation": {
        "id": 15788000002076,
        "type": "sale",
        "status": "success",
        "date": "2022-03-03T10:50:29+0000",
        "sum_initial": {
            "amount": 1815,
            "currency": "EUR"
        },
        "code": "0",
        "message": "Success"
    },
    "signature": "zwoFECy7H+WZFziw9IgR90SEmC+o3qXD7OIaHqgQuqcSwBC09W6yQA=="
}

signer = Signer()
actualSign = params['signature']
del params['signature']
expectedSign = signer.getSign(params, secretKey).decode()

print('Actual signature:' + actualSign)
print('Expected signature:' + expectedSign)

if (expectedSign == actualSign):
    print('Signature is correct')
else:
    print('Signature is incorrect')

Описания используемых статусов платежей можно найти в отдельном разделе, а описание оповещений и работы с ними — в отдельной статье. При этом важно помнить, что в случаях, когда пользователь не подтверждает оплату в платёжной форме, платёж не регистрируется (и статуса по нему тоже нет).

Извлечённую из оповещений информацию можно использовать для оперативного обновления статусов заказов в веб-сервисе, информирования пользователей и других целей — в соответствии с моделью работы вашего веб-сервиса.

Рис.: Java

1 Обеспечиваем подписывание данных

// Кодирование алгоритма
const CryptoJS = require('crypto-js'); // npm install crypto-js
 
class Signer {
    getSign(params, key) {
        const paramsPrepared = this.getParamsToSign(params, 1);
 
const joinedString = Object.values(paramsPrepared).join(';');
 
const hash = CryptoJS.HmacSHA512(joinedString, key);
 
return hash.toString(CryptoJS.enc.Base64);
    }
 
getParamsToSign(params, currentLevel = 1, prefix = '', useUnlimitedRecursionDepth = false) {
        let paramsToSign = {};
        let value = '';
 
let keys = Object.keys(params);
        let key = '';
        for (let i = 0; i < keys.length; i++) {
            key = keys[i];
 
let paramKey = (prefix ? prefix + ':' : '') + key;
 
if (typeof params[key] === 'object') {
                if (!useUnlimitedRecursionDepth && (currentLevel >= 3)) {
                    paramsToSign[paramKey] = paramKey + ':';
                } else {
                    let subParams = this.getParamsToSign(params[key], currentLevel + 1, paramKey, useUnlimitedRecursionDepth);
 
Object.assign(paramsToSign, subParams);
                }
            } else {
                if (typeof params[key] === "boolean") {
                    value = params[key] === true ? '1' : '0';
                } else {
                    value = params[key].toString();
                }
 
paramsToSign[paramKey] = paramKey + ':' + value;
            }
        }
 
let orderedKeys = Object.keys(paramsToSign).sort();
 
let ordered = {};
 
for (let k = 0; k < orderedKeys.length; k++) {
            ordered[orderedKeys[k]] = paramsToSign[orderedKeys[k]];
        }
 
return ordered;
    }
}
 
module.exports = Signer;


// Пример использования

// указание параметров вызова платёжной формы и секретного ключа
const signer  = require('./Signer');
 
let params = {
    'project_id': 57123,
    'payment_amount': 1815,
    'payment_currency': 'EUR',
    'customer_id': 'customer_112',
    'payment_id': 'payment_443',
};
 
// формирование подписи
params['signature'] = (new signer()).getSign(params, '<secret_key>');

// получение ссылки для вызова платёжной формы
const uriParams = Object.keys(params).sort().reduce(
    (paramsArray, key) => {
        paramsArray.push(key + '=' + encodeURIComponent(params[key]));
        return paramsArray;
    },
    []
);
const link = ['https://paymentpage.ecommpay.com/payment', uriParams.join('&')].join('?');

 

2 Обеспечиваем приём оповещений

Чтобы оперативно узнавать о результатах платежей и получать другую значимую информацию, следует настроить приём и обработку программных оповещений от платёжной платформы. Это делается в три шага:

  1. Определяем и задаём в платёжной платформе адрес для приёма веб-сервисом оповещений по проекту (сначала можно задать один общий адрес, а после — несколько, под разные события). Для этого открываем в интерфейсе Dashboard раздел Проекты и используем инструменты на вкладке Оповещения.
  2. Настраиваем проверку целостности и разбор оповещений, поступающих на указанный URL, с использованием SDK.
  3. Настраиваем отправку синхронных HTTP-ответов о приёме оповещений: 200 ОК если подпись корректна и 400 Bad Request если подпись некорректна.
// Работа с оповещениями. Пример сверки полученной подписи с расчётной

const signer = require('./Signer');

const secretKey = '<secret_key>';

const params = {
    "project_id": 57123,
    "payment": {
        "id": "payment_443",
        "type": "purchase",
        "status": "success",
        "date": "2022-03-03T10:50:29+0000",
        "method": "card",
        "sum": {
            "amount": 1815,
            "currency": "EUR"
        },
        "description": "Gagarin set"
    },
    "customer": {
        "id": "customer_112",
        "ip": "127.0.0.1",
        "phone": "+60-3-xxxx-xxxx",
        "email": "jane.doe@testmail.com"
    },
    "operation": {
        "id": 15788000002076,
        "type": "sale",
        "status": "success",
        "date": "2022-03-03T10:50:29+0000",
        "sum_initial": {
            "amount": 1815,
            "currency": "EUR"
        },
        "code": "0",
        "message": "Success"
    },
    "signature": "zwoFECy7H+WZFziw9IgR90SHvCuvqXD7OIaHqgQuqcSwBC09W6yQA=="
};

const actualSignature = params['signature'];
delete params.signature;
const expectedSignature = (new signer()).getSign(params, secretKey);

console.log('Actual: ' + actualSignature);
console.log('Expected: '+expectedSignature);

if (actualSignature == expectedSignature) {
    console.log('Signatures is correct');
} else {
    console.log('Signature is incorrect');
}

Описания используемых статусов платежей можно найти в отдельном разделе, а описание оповещений и работы с ними — в отдельной статье. При этом важно помнить, что в случаях, когда пользователь не подтверждает оплату в платёжной форме, платёж не регистрируется (и статуса по нему тоже нет).

Извлечённую из оповещений информацию можно использовать для оперативного обновления статусов заказов в веб-сервисе, информирования пользователей и других целей — в соответствии с моделью работы вашего веб-сервиса.

Действия в клиентской части

Открывать Payment Page в клиентской части веб-сервиса можно по-разному, и для наглядности можно попробовать несколько способов даже в рамках быстрого старта.

Самый простой способ — открывать платёжную форму в отдельной вкладке браузера.

Рис.: Открытие в отдельной вкладке браузера

Чтобы открыть платёжную форму в виде отдельной HTML-страницы, следует использовать ссылку, полученную при подписывании данных. Это ссылка формата https://paymentpage.ecommpay.com/payment?<parameters>, где <parameters> — строка с названиями и значениями параметров вызова.

HTTP-запрос с применением метода GET в таком случае может выглядеть следующим образом:

GET
/payment?payment_currency=EUR&project_id=42&payment_amount=1000&
customer_id=123&payment_id=4438&signature=AE5hmtzdP0Dt7qGTg... HTTP/1.1

Host: https://paymentpage.ecommpay.com

Для использования других способов — с открытием платёжной формы в модальном окне и в элементе iframe — необходимо подключить две библиотеки: CSS для корректного отображения формы и JavaScript для вызова формы. Это делается через добавление ссылок на библиотеки в заголовочной части HTML-страницы.

Рис.: Подключение библиотек

<head>
...
// подключение CSS-библиотеки
<link rel="stylesheet" href="https://paymentpage.ecommpay.com/shared/merchant.css" /> 

// подключение JavaScript-библиотеки
<script type="text/javascript" src="https://paymentpage.ecommpay.com/shared/merchant.js">
</script>
...
</head>

Также для вызова платёжной формы с помощью JavaScript-библиотеки необходимо использовать подписанный набор параметров в виде JavaScript-объекта. Его можно формировать из ссылки, полученной при подписывании данных (в PHP для этого можно использовать функции parse_url и parse_str), или используя серверный код для формирования подписи (без составления ссылки) и добавляя подпись к остальным параметрам.

Подключив библиотеки и разобравшись с параметрами, можно пробовать.

Рис.: Открытие в модальном окне

Чтобы открыть Payment Page в модальном окне по щелчку кнопки на странице веб-сервиса, можно использовать метод bind JavaScript-объекта EPayWidget. При обращениях к этому объекту следует указывать идентификатор кнопки (pay_button_id), объект с параметрами вызова Payment Page (configObj) и HTTP-метод отправки запросов (method):

EPayWidget.bind('pay_button_id',            // идентификатор кнопки
                { payment_id: 'payment_443',         // идентификатор платежа
                  payment_amount: 1815,              // сумма платежа
                  payment_currency: 'EUR',           // код валюты платежа
                  project_id: 57123,                 // идентификатор проекта
                  customer_id: 'customer_112',       // идентификатор пользователя
                  signature: 'YWb6Z20ByxpQ30hfTI' }, // подпись
                 'post')  // HTTP-метод   

Рис.: Открытие непосредственно на странице, в элементе iframe

Чтобы открыть Payment Page в элементе iframe по щелчку кнопки на странице веб-сервиса, как и в случае с модальным окном, можно использовать метод bind JavaScript-объекта EPayWidget. При обращениях к этому объекту следует указывать идентификатор кнопки (pay_button_id), объект с параметрами вызова Payment Page (configObj), идентификатор элемента, в котором необходимо отобразить платёжную форму (target_element), и HTTP-метод отправки запросов (method):

EPayWidget.bind('pay_button_id',             // идентификатор кнопки
                { payment_id: 'payment_443',          // идентификатор платежа
                  payment_amount: 1815,               // сумма платежа
                  payment_currency: 'EUR',            // код валюты платежа
                  project_id: 57123,                  // идентификатор проекта
                  customer_id: 'customer_112',        // идентификатор пользователя
                  signature: 'YWb6Z20ByxpQ30hfTI' },  // подпись
                  target_element: 'widget-container', // идентификатор элемента
                 'post') // HTTP-метод  

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

Тестирование

Когда мы открываем платёжную форму в рабочем режиме, с ней работает пользователь. А в тестовом режиме можно почувствовать себя на его месте и протестировать разные сценарии оплаты с его позиции. Главный вопрос при этом — какие реквизиты указывать?

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

В базовом случае можно использовать следующие тестовые номера платёжных карт:

  • 4000 0000 0000 0077 — для проведения оплаты;
  • 4111 1111 1111 1111 — для отклонения оплаты.


Для более масштабного тестирования можно использовать развёрнутые тестовые данные для карточных и альтернативных платежей, а также произвольные данные, включая реквизиты реальных карт, кошельков и других платёжных инструментов. Это безопасно, поскольку для всех данных в тестовой среде обеспечивается тот же уровень защиты, что и в рабочей, но реальные платежи при этом не проводятся.

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

Дополнения

Контроль проведения платежей

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

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

Возврат средств

Если по какой-либо проведённой оплате необходимо выполнить возврат, для этого также можно использовать Dashboard. При тестировании работы с платёжной платформой можно попробовать выполнить полные и частичные возвраты из карточек платежей.

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

Возможности управления формой

При работе с Payment Page можно оперировать различными возможностями, чтобы управлять видом и поведением формы, например, задавая подходящий язык, фильтруя методы оплаты или управляя способами возвращения пользователя к веб-сервису. Некоторые из таких возможностей требуют подключения через специалистов ecommpay, иные можно использовать без обращений к кому-либо, просто настроив их через Dashboard или передавая дополнительные параметры в запросах на открытие Payment Page.

При тестировании работы с Payment Page мы рекомендуем попробовать следующее:

  • Настроить оформление платёжной формы с помощью конструктора.

    Для этого следует перейти в раздел Проекты интерфейса Dashboard и воспользоваться инструментами вкладки Payment Page Designer (подробнее — в статье Индивидуальное оформление).

  • Попробовать вызов платёжной формы на разных языках.

    Чтобы открыть Payment Page на определённом языке, его код (в двухбуквенном формате ISO 639-1 alpha-2) следует передать в значении соответствующего параметра: language_code. Список поддерживаемых ecommpay языков можно найти здесь.

  • Возвращать пользователя к веб-сервису после проведения платежей.

    После проведения платежей в зависимости от их результатов можно перенаправлять пользователей к разным страницам веб-сервиса. Адреса таких страниц можно задать в разделе Проекты интерфейса Dashboard (во вкладке Ссылки для перенаправления), либо передавать их в конкретных запросах в значении параметров merchant_success_url и merchant_fail_url.

  • И далее на ваш вкус.

    Информацию о разных возможностях можно найти в общем обзоре и в специализированном разделе, а полный перечень параметров вызова — в отдельной статье. С вопросами же, как обычно, можно обращаться к нашим специалистам.

Запуск

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

Успехов!