© Никита Сенченко
<<XML-интерфейсы WebMoney. Часть 1. WMSigner.
В первой статье мы познакомились с модулем аутентификации WMSigner и создали php-функции для работы c ним и получения электронной подписи. На данный момент у нас есть: установленный на сервере, настроенный и работающий WMSigner, а также скрипт с функциями wmxml.inc.php.
Теперь можно переходить непосредственно к изучению XML-интерфейсов WebMoney.
XML-интерфейсы
Как мы уже говорили, XML-интерфейсы WebMoney - это "язык", на котором ваш сайт может "общаться" с сервером WebMoney от вашего имени и отдавать ему команды на совершение тех или иных действий. Использование XML-интерфейсов позволяет автоматизировать все процессы на вашем сайте, связанные с WebMoney. А именно:
| Название XML-интерфейса | Что делает |
| X1 описание | выписывает счет другому пользователю WebMoney от вашего WMID |
| X2 описание | выполняет перевод с вашего кошелька |
| X3 описание | получает историю транзакций по кошельку |
| X4 описание | получает историю исходящих (выписанных вами) счетов |
| X5 описание | завершает входящий перевод с протекцией |
| X6 описание | отправляет сообщение по внутренней WM-почте |
| X7 описание | производит авторизацию пользователя Keeper Classic на вашем сайте |
| X8 описание | проверяет существование в системе WMID/кошелька; получает WMID, соответствующий кошельку |
| X9 описание | получает текущий баланс на вашем кошельке |
| X10 описание | получает историю входящих (выписанных вам) счетов |
| X11 описание | получает аттестационную информацию заданного WMID |
Все интерфейсы требуют наличия персонального аттестата. Кроме того, некоторые интерфейсы перед началом использования нужно активировать, отправив на WMID техподдержки 941977853154 запрос с указанием: url сайта, IP-адреса сервера, краткой информации о вашем веб-проекте, в каких целях будет использоваться интерфейс.
Начало работы
Все XML-интерфейсы работают по следующему принципу:
- Вы формируете на своем сайте XML-пакет, содержащий параметры запроса (например: "отправить сообщение с текстом text пользователю wmid"). В запрос вы вставляете также электронную подпись XML-пакета, сформированную модулем WMSigner. Это позволяет посылать запросы только от имени вашего WMID.
- Сформированный XML-пакет направляете серверу WebMoney методом POST.
- Сервер WebMoney расшифровывает подпись вашего XML-запроса и убеждается, что запрос прислали именно вы. Таким образом, никто кроме вас не сможет, например, от вашего имени списать деньги с вашего кошелька. Но и вы не сможете списать деньги с чужого кошелька от лица его владельца.
- Если валидность подписи установлена и запрос составлен верно, то сервер WebMoney выполняет запрос и отвечает вам XML-пакетом, содержащим результаты выполнения запроса (например: "успешно отправил сообщение с текстом text пользователю wmid и датировал датой date").
- Разбираете (парсите) ответ так, как вам это нужно.
Итак, нам нужно выполнить 3 действия: сформировать XML-запрос, отправить его серверу WebMoney и получить ответ, разобрать XML-ответ. Сформировать. Отправить и получить. Разобрать.
Формировать запрос можно любым методом, главное, чтобы сформированный XML-пакет отвечал требованиям стандарта XML. Можно воспользоваться функциями DOM/DOM XML для PHP, однако мы поступим проще. Формировать тело запроса будем прямо в строке скрипта, например:
При рассмотрении конкретных интерфейсов мы увидим, что почти все они требуют обязательного указания в XML-запросе поля <reqn> с уникальным номером запроса. Причем, каждый новый запрос должен содержать reqn больший, чем в предыдущем запросе. Сам reqn должен быть целым числом и иметь до 15 цифр. Для генерации такого номера удобно использовать функцию, основанную на текущей отметке времени, которая, как известно, постоянно растет и никогда не повторяется. Чтобы два запроса, отправленные в одну секунду, не получили одинаковый reqn, будем брать эту отметку с точностью до микросекунд.
Поместим такую функцию в наш wmxml.inc.php:
Внимание! Не начинайте экспериментировать с XML-интерфейсами WebMoney без этой функции! Если вы хотя бы раз передадите большой reqn (например, 777777777777777), то в дальнейшем не сможете передавать reqn, который будет меньше этого. В такой ситуации вам придется либо изобретать функцию, генерирующую постоянно растущий reqn больше 777777777777777, либо писать в тех. поддержку с просьбой обнулить для вас счетчик reqn.
Итак, как сформировать пакет - понятно. Теперь его нужно передать серверу WebMoney. Делать это нужно методом POST. Есть несколько вариантов реализации. Мы воспользуемся библиотекой libcurl (CURL). Как правило, она включена в сборку PHP даже на недорогих виртуальных хостингах. Проверить, включена ли поддержка CURL на вашем сервере, можно с помощью функции phpinfo() или так: echo function_exists("curl_init");.
Напишем функцию _GetAnswer(), которая будет отправлять на сервер WebMoney XML-пакет с запросом и получать ответ. Включим функцию в наш модульный файл wmxml.inc.php.
Обратите внимание на строки:
Чтобы (в целях безопасностии и защиты от DNS-атак) во время сеанса CURL мог проверить валидность сертификата на сервере WebMoney, мы используем аргументы CURLOPT_CAINFO и CURLOPT_SSL_VERIFYPEER. Для этого скачиваем корневой сертификат WebMoney отсюда: www.wmcert.com/Cert/WebMoneyCA.crt, заливаем его в любой каталог на сервере (можно в созданный ранее /home/site.ru/data/signer/), указываем путь до него в CURLOPT_CAINFO.
Будьте внимательны! Если вы скачиваете сертификат через Internet Explorer, то он автоматически переименуется и сохранится как WebMoneyCA.cer. Не забудьте в этом случае прописать правильный путь:
На вход функции _GetAnswer() подается не только XML-пакет с запросом, но и URL на сервере WebMoney, куда нужно передавать этот пакет. Для каждого интерфейса URL свой, отдельный. Создадим в файле wmxml.inc.php массив с адресами всех интерфейсов для удобства:
Функция _GetAnswer() получила ответ от сервера WebMoney и вернула его на выходе:
Переходим к последнему этапу. Ответ имеет вид XML-пакета. Нам нужно прочитать из него те поля, которые нам нужны, то есть разобрать (отпарсить) XML. Сделать это можно любым XML-разборщиком PHP: DOM XML, libxml, XML expat parsers и др. Автор этой статьи привык работать с простой и удобной библиотекой SimpleXML (поддерживается только в PHP5, но не в PHP4), поэтому все примеры в статье будут написаны с использованием функций именно этой библиотеки. Вы можете ко мне присоединиться, либо выбрать другое средство разбора XML, которое вам по душе.
На данный момент наш модульный файл wmxml.inc.php содержит следующий код:
Ниже мы познакомимся с некоторыми XML-интерфейсами WebMoney. Надеюсь, вы поймете смысл того, что мы делаем, и сможете наладить работу с остальными интерфейсами самостоятельно.
Описывать интерфейсы мы будем один за другим, но в описании каждого из них будем повторять одни и те же правила и пояснения, поэтому вы можете начинать с любого интерфейса, а не изучать их по очереди.
Интерфейс X2. Отправка переводов.
С помощью этого интерфейса вы можете переводить средства со своего кошелька. Полное описание интерфейса находится здесь. Интерфейс требует включения путем обращения в службу поддержку WMID 941977853154.
Наш XML-запрос должен выглядеть так:
Что означают параметры:
- reqn - номер запроса, всякий раз должен быть больше предыдущего;
- wmid - ваш WMID;
- sign - подпись запроса, сформированная из параметров: reqn + tranid + pursesrc + pursedest + amount + period + pcode + desc + wminvid;
- tranid - уникальный номер перевода;
- pursesrc - номер вашего кошелька, с которого выполняется перевод;
- pursedest - номер кошелька, на который выполняется перевод;
- amount - сумма перевода;
- period - срок протекции в днях;
- pcode - код протекции;
- desc - примечание перевода;
- wminvid - номер счета в системе WebMoney, по которому выполняется перевод.
Формат ответа сервера WebMoney можно посмотреть в описании интерфейса. Нас интересует, фактически, только одно поле ответа - <retval>. Если оно равно 0, то запрос выполнен успешно. В противном случае, оно будет содержать код ошибки, расшифровку которой можно посмотреть в том же описании.
Приведем теперь полностью функцию, которая реализует работу с интерфейсом X2 и добавим её в wmxml.inc.php:
Разберем, что происходит в этой функции.
Функция получает переменные:
- $tranid - номер транзакции в системе учета вашего сайта. Может быть любым целым числом длиной 32 бита, то есть от 0 до 2147483647 (отрицательные числа от 0 до -2147483647 тоже возможны, хотя официальным описанием это запрещено). $tranid не должен повторяться более одного раза. Вам нужно организовать сохранение предыдущего $tranid на вашем сайте, например, в базе данных и увеличивать его всякий раз при создании очередной транзакции.
- $purse - ваш кошелек, с которого списываются средства. Кошелек должен быть из числа тех, что принадлежат вашему WMID, фигурирующему в поле <wmid> XML-запроса.
- $rpurse - кошелек получателя средств.
- $amount - сумма перевода. В качестве разделителя дробной части используйте точку, а не запятую. Незначащие нули в конце должны отсутствовать (например, 10.5 - верно, 10.50 - вызовет ошибку).
- $period - количество дней протекции. Если перевод без протекции, то устанавливайте в 0.
- $pcode - код протекции до 255 символов без пробелов в начале и конце. Допускаются любые символы, в том числе русские буквы. Если $period=0 (то есть перевод без протекции), то $pcode обязательно должен быть пустым. $pcode желательно передавать в функцию в кодировке Win1251.
- $desc - примечание к переводу до 255 символов без пробелов в начале и конце. Допускаются любые символы, в том числе русские буквы. $desc желательно передавать в функцию в кодировке Win1251.
- $wminvid - номер счета в системе WebMoney, по которому выполняется перевод. Если перевод не по счету, то поле должно быть равно 0.
Генерируем уникальный номер запроса $reqn с помощью функции _GetReqn():
Выполняем некоторые преобразования, чтобы избежать ошибок. У $desc и $pcode убираем лишние пробелы в начале и конце. У $amount удаляем незначащие нули, если они есть. Этого требует описание интерфейса.
Получаем подпись XML-пакета с помощью функции _GetSign(). На вход функции подаем строку, полученную в результате склейки параметров, как это предусмотрено в описании интерфейса. Параметры должны склеиваться именно в таком порядке, как это указано ниже. Значения всех параметров при формировании строки подписи обязательно должны быть в кодировке Win1251! Поэтому - внимание! - если функция _WMXML2(), с которой мы сейчас работаем, получила переменные $desс или $pcode в кодировке, отличной от Win1251, то их нужно сперва перекодировать. Остальные параметры содержат всегда только цифры и английские буквы, поэтому для них кодировка никакой роли не играет.
Теперь преобразуем специальные символы ("<", "&" и др.) в html-сущности. Если этого не сделать, то при попадании в $desc или $pcode таких символов WebMoney наш запрос не примет и вернет ошибку "A name was started with an invalid character". Обратите внимание, что при формировании строки подписи переменные $desc и $pcode пребывали в своем первозданном виде, со всеми спецсимволами, а преобразование мы выполняем уже после получения подписи. Преобразовать спецсимволы в PHP можно с помощью функции htmlspecialchars():
Но это еще не всё. $desc и $pcode могут содержать русские символы. Например, в $desc может быть такой текст: "тестовый товар & тестовая услуга". И здесь нужно понимать, как подготовить $desc и $pcode для передачи в составе XML-пакета. Дело в том, что содержимое полученного от вас XML сервер WebMoney попытается прочитать так, будто он пришел в кодировке Unicode. Если сервер встретит в пакете русские символы в другой кодировке, то вернет ошибку: "An invalid character was found in text content" ("Обнаружен ошибочный символ"). Для того чтобы этого не произошло, нам нужно принудительно перекодировать $desc и $pcode в UTF-8 и уже в таком виде включать их в XML-запрос.
Сделать преобразование кодировок можно с помощью функции iconv() из одноименного расширения PHP:
То же самое, но с помощью функции mb_convert_encoding() из расширения mbstring:
Можно даже преобразовать не в UTF-8, а в html-сущности, это тоже сработает:
Если же iconv и mbstring вашим сервером не поддерживаются, то могу предложить еще один вариант. Оставьте $desc и $pcode в кодировке Win1251, а в начале XML-запроса вставляйте заголовок <?xml version='1.0' encoding='windows-1251'?>. Он укажет, что данные в пакете переданы в Win1251, и сервер WebMoney обработает их именно в этой кодировке. То есть пакет будет начинаться так:
Правда, хотя на момент написания этих строк такой вариант работал нормально, не могу поручиться, что он будет работать всегда, поскольку точная логика обработки XML-пакета сервером WebMoney не известна, и не ясно, будет ли всегда сервер учитывать заголовок XML-запроса.
Понятно, что если переменные $desc и $pcode были переданы в функцию _WMXML2(), уже пребывая в кодировке UTF-8, то никаких преобразований делать не нужно.
Наконец, формируем XML-пакет с запросом:
Отправляем запрос на сервер WebMoney и получаем от него ответ с помощью функции _GetAnswer(). На вход функции подаем URL интерфейса X2 из глобального массива $XML_addr, а также пакет XML-запроса, сформированный только что:
Для отладки и поиска ошибок может потребоваться прочитать XML-ответ "в чистом виде". Тогда просто раскомментируйте следующую строку:
Вызовом функции simplexml_load_string() из библиотеки SimpleXML создаем на основе XML-ответа, полученного от WebMoney, объект. Параметры XML-ответа становятся свойствами объекта, и мы сможем легко получать к ним доступ.
Если $xmlres не создан, значит, мы по какой-то причине не получили ответ от WebMoney. Тогда прерываем работу функции:
Читаем следующие свойства объекта: retval (содержит результат выполнения запроса; если перевод на кошелек выполнен успешно, то retval равен 0), retdesc (содержит расшифровку результата), operation/datecrt (содержит дату и время создания транзакции в системе WebMoney в случае успешного перевода, либо дату и время последнего успешного перевода в случае ошибки). Сохраняем эти переменные в массив $result.
Обратите внимание, что содержимое поля <retdesc> мы перекодировали из UTF-8 в Win1251. Дело в том, что XML-ответ от WebMoney приходит в кодировке Windows1251, но SimpleXML при помещении XML-данных в объект принудительно превратил их в Юникод. Такая вот у него особенность. А так как <retdesc> - это строка, и теоретически она может содержать русские символы, то при выемке её из объекта мы возвращаем ей "родную" кодировку. Хотя, в общем, это не обязательно и зависит от ваших нужд и задач.
На выходе функция _WMXML2() возвращает массив $result:
Теперь осталось только проверить, как работает то, что мы написали. Создадим скрипт для тестов test.php:
"; echo "Дата и время - ".$r['date']."
"; ?>
Этот код должен выполнить перевод 0.01 WM без протекции, с примечанием "тестовый товар & тестовая услуга", с кошелька_отправителя на кошелек_получателя.
Естественно, на кошельке-отправителе $purse должно быть достаточно средств для совершения перевода.
Комментариев нет:
Отправить комментарий