ВНИМАНИЕ! Полнофункциональный модуль для PHP - WMXI, включая реализацию функциональности WMSigner на языке PHP, включая реализацию обращения ко всем открытым XML-интерфейсам системы, для обоих типов ключа (Classic, Light) может быть скачан здесь.
ПРИМЕР обращения к интерфейсу X9 (Получение информации о балансе на кошельках) на языке PHP, с использованием бинарного модуля WMSigner (реализация на С++) может быть скачан здесь.
Прокомментируем основные моменты кода в этом примере.
Проверка работоспособности WMSigner
Модуль WMSigner используется при подписи запроса ключом WM Keeper Classic. Ознакомьтесь с подробной информации о модуле WMSigner, получении ключей и настройке. Ниже описывается, как можно проверить его работоспособность из PHP. Вызов модуля происходит из функции wm_GetSign(), на вход она получает строку для подписи, возвращает строку из 132-х символов, либо текст ошибки, либо пустую строку в случае когда невозможно вызвать модуль WMSigner.
<? echo wm_GetSign('123'); ?>
Формирование XML-запроса
Формировать XML-запрос можно любым способом, главное чтобы результат соответствовал синтаксису XML и формату запроса. Воспользуемся функциями DOM XML.
<?
$reqID = wm_ReqID();
$doc = domxml_new_doc('1.0');
$root = $doc->create_element('w3s.request');
$root = $doc->append_child($root);
$tmp = $doc->create_element('reqn');
$tmp->set_content($reqID);
$root->append_child($tmp);
$tmp = $doc->create_element('wmid');
$tmp->set_content($wmWMID);
$root->append_child($tmp);
$getpurses = $doc->create_element('getpurses');
$root->append_child($getpurses);
$tmp = $doc->create_element('wmid');
$tmp->set_content($wmWMID);
$getpurses->append_child($tmp);
$request = $doc->dump_mem(TRUE);
?>
Функция wm_ReqID() генерирует уникальный идентификатор запроса, основываясь на функциях времени.
Выполнение запросов к сертификационному центру
Запросы к разным интерфейсам похожи друг на друга и отличаются лишь URL-ом, форматом XML-запроса и возвращаемым результатом. Приведем код функции осуществляющей вызов CURL:
<?
function wm_xmlHttpsReq($addr, $xml){
# Использовать переменные из конфигурационного файла config.php
global $wmURL, $wmCAcert, $wmType, $wmKey, $wmCert, $wmPass;
# Инициализируем CURL нужным URL
$ch = curl_init($wmURL.$addr);
# В выводе CURL http-заголовки не нужны
curl_setopt($ch, CURLOPT_HEADER, 0);
# Возвращать результат, а не выводить его в stdout
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
# Метод http-запроса POST
curl_setopt($ch, CURLOPT_POST,1);
# Данные запроса
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
# Укажем путь к корневому сертификату WebMoney CA:
curl_setopt($ch, CURLOPT_CAINFO, $wmCAcert);
# Внимание! Не используйте curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE)!
# Это дает возможность осуществить атаку с подменой DNS.
# Выполнить запрос
$result=curl_exec($ch);
# Обработаем возможные ошибки
if( curl_errno($ch) != 0 ) {
die('CURL_error: ' . curl_errno($ch) . ', ' . curl_error($ch));
};
# Закроем обработчик
curl_close($ch);
return $result;
}
?>
Разбор полученного ответа
Ниже приведена структура ответа сервера:
<w3s.response>
<reqn></reqn> - номер запроса, на который высылается ответ
<retval></retval> - код ошибки выполнения запроса: 0 - выполнен успешно
<retdesc></retdesc> - описание ошибки если retval != 0
<тип_ответа>
... - параметры ответа
</тип_ответа>
</w3s.response>
Осуществлять разбор XML-ответа можно любым удобным методом (SAX, DOM, Regexp и т.д.) В данном случае был использован способ через функцию PHP xml_parse_into_struct():
<?
$xml_parser = xml_parser_create('UTF-8');
# Парсим XML в переменные $vals, $index
xml_parse_into_struct($xml_parser, $result, $vals, $index);
xml_parser_free($xml_parser);
# Проверим ответ на корректность
$check = wm_CheckResp($vals,$index);
if( $check != 0) die($check);
# Поместим список кошельков, их описаний, и сумм на них в переменную $purses
$purses = array();
$pursename = '';
$amount = '';
$desc = '';
for($n = 0; $n < count($vals); ++$n){
$e = $vals[$n];
switch ($e['tag']){
case 'PURSE':
switch ($e['type']){
case 'open':
$pursename = ''; $amount = ''; $desc = '';
break;
case 'close':
$purses[$pursename] = array('amount' => $amount, 'desc' => $desc);
break;
}
break;
case 'PURSENAME':
$pursename = $e['value'];
break;
case 'AMOUNT':
$amount = $e['value'];
break;
case 'DESC':
$desc = $e['value'];
break;
}
}
?>
Работа с сертификатами WM Keeper Light (X.509)
После получения и инсталляции в браузер сертификата Light, его необходимо экспортировать в файл. Это будет файл формата PKCS12 c расширением .pfx или .p12. В этом файле содержится приватный ключ и сам сертификат.
Далее его необходимо сконвертировать в формат PEM для работы с CURL. Воспользуемся для этого утилитой openssl из одноименного пакета:
$ openssl pkcs12 -in 351237877840.pfx -out 351237877840.key -nocerts
$ openssl pkcs12 -in 351237877840.pfx -out 351237877840.cer -clcerts -nokeys
Таким образом получили файлы .key и .cer, которые и будем использовать при запросе к w3s. В опции CURL добавляются:
<?
curl_setopt($ch, CURLOPT_SSLKEY, $wmKey);
curl_setopt($ch, CURLOPT_SSLKEYPASSWD , $wmPass);
curl_setopt($ch, CURLOPT_SSLCERT, $wmCert);
?>
Где $wmPass - пароль который Вы указали при преобразовании ключа в PEM формат.
Сам XML-запрос тоже меняется. Из него пропадет элемент <sign/>
Важные примечания
Хранить файлы ключей (в том числе Light) необходимо в каталоге недоступном для скачивания пользователями!!!
1. WMSigner, его конфиг (и файл ключей) нужно располагать в каталоге недоступном для скачивания пользователями.
2. Конфиг WMSigner.ini модуль подписи ищет в том каталоге где лежит сам.
3. Файл ключей WMSigner будет искать как указано в WMSigner.ini, с учетом того что "текущим" считается каталог расположения скрипта вызывающего WMSigner.
Все вышеперечисленное должно пониматься с учетом прав доступа Unix.
ПРИМЕР:
Каталог скриптов и html-документов: /home/my_site/html
Каталог расположения WMSigner: /home/my_site/sign
Конфигурационный файл: /home/my_site/sign/WMSigner.ini :
123456789012
pass
/home/my_site/sign/keyfile.kwm