• Гость, поддержи форум! Нажимай на рекламу!
Поддержи проект! Нажми!

WebMoney - Интерфейс X2. Отправка переводов.

pligin

Команда форума
Администратор
#1
С помощью этого интерфейса вы можете переводить средства со своего кошелька. Полное описание интерфейса находится здесь. Интерфейс требует включения - подробности в теме Подключение интерфейса X2 в Merchant.WebMoney.

Наш XML-запрос должен выглядеть так:
Код:
<w3s.request>
    <reqn></reqn>
    <wmid></wmid>
    <sign></sign>
    <trans>
        <tranid></tranid>
        <pursesrc></pursesrc>
        <pursedest></pursedest>
        <amount></amount>
        <period></period>
        <pcode></pcode>
        <desc></desc>
        <wminvid></wminvid>
        <onlyauth></onlyauth>
    </trans>
</w3s.request>
Что означают параметры:
  • reqn - номер запроса, всякий раз должен быть больше предыдущего;
  • wmid - ваш WMID;
  • sign - подпись запроса, сформированная из параметров: reqn + tranid + pursesrc + pursedest + amount + period + pcode + desc + wminvid;
  • tranid - уникальный номер перевода;
  • pursesrc - номер вашего кошелька, с которого выполняется перевод;
  • pursedest - номер кошелька, на который выполняется перевод;
  • amount - сумма перевода;
  • period - срок протекции в днях;
  • pcode - код протекции;
  • desc - примечание перевода;
  • wminvid - номер счета в системе WebMoney, по которому выполняется перевод;
  • onlyauth - признак, который определяет, будет ли выполнен перевод, если получатель запрещает входящие переводы.
Формат ответа сервера WebMoney можно посмотреть в описании интерфейса. Нас интересует, фактически, только одно поле ответа - <retval>. Если оно равно 0, то запрос выполнен успешно. В противном случае, оно будет содержать код ошибки, расшифровку которой можно посмотреть в том же описании.

Приведем теперь полностью функцию, которая реализует работу с интерфейсом X2 и добавим её в wmxml.inc.php:
PHP:
// ИНТЕРФЕЙС X2. ОТПРАВКА ПЕРЕВОДА.
// На выходе: массив ['retval'=>код выполнения, 'retdesc'=>описание результата, 'date'=>дата и время]
function _WMXML2 ($tranid,$purse,$rpurse,$amount,$period,$pcode,$desc,$wminvid,$onlyauth) {
    global $Global_WMID, $XML_addr;
    $reqn=_GetReqn();
    $desc=trim($desc); $pcode=trim($pcode); $amount=floatval($amount);
    $rsign=_GetSign($reqn.$tranid.$purse.$rpurse.$amount.$period.$pcode.$desc.$wminvid);
    $pcode=htmlspecialchars($pcode, ENT_QUOTES);
    $desc=htmlspecialchars($desc, ENT_QUOTES);
    $pcode=iconv("CP1251", "UTF-8", $pcode);
    $desc=iconv("CP1251", "UTF-8", $desc);
    $xml="
    <w3s.request>
        <reqn>$reqn</reqn>
        <wmid>$Global_WMID</wmid>
        <sign>$rsign</sign>
        <trans>
            <tranid>$tranid</tranid>
            <pursesrc>$purse</pursesrc>
            <pursedest>$rpurse</pursedest>
            <amount>$amount</amount>
            <period>$period</period>
            <pcode>$pcode</pcode>
            <desc>$desc</desc>
            <wminvid>$wminvid</wminvid>
            <onlyauth>$onlyauth</onlyauth>
        </trans>
    </w3s.request>";
    $resxml=_GetAnswer($XML_addr[2], $xml);
    // echo $resxml;
    $xmlres = simplexml_load_string($resxml);
    if(!$xmlres) {
        $result['retval']=1000;
        $result['retdesc']="Не получен XML-ответ";
        return $result;
    }
    $result['retval']=strval($xmlres->retval);
    $result['retdesc']=iconv("UTF-8", "CP1251", strval($xmlres->retdesc));
    $result['date']=strval($xmlres->operation->datecrt);
    return $result;
}
Разберем, что происходит в этой функции.

Функция получает переменные:
  • $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.
  • $onlyauth - если это поле равно 0, то перевод выполнится даже в том случае, если получатель запретил (в настройках своего WM Keeper) прием входящих переводов от неавторизованных корреспондентов и наш WMID окажется неавторизованным. Если это поле равно 1, то перевод выполнится только в том случае, если получатель разрешил переводы от неавторизованных, либо если наш WMID авторизован отправителем.
Примечание. Запрет на прием платежей от неавторизованных корреспондентов обычно устанавливают продавцы, которые используют автоматизированный прием WebMoney посредством WM Merchant и хотят избежать поступления на кошельки прямых переводов, которые не ожидаются ("в обход" WM Merchant), чтобы не искать их потом вручную. Из этих соображений, для соблюдения бизнес-этики, рекомендуем передавать интерфейсу всегда onlyauth=1.
Генерируем уникальный номер запроса $reqn с помощью функции _GetReqn():
PHP:
$reqn=_GetReqn();
Выполняем некоторые преобразования, чтобы избежать ошибок. У $desc и $pcode убираем лишние пробелы в начале и конце. У $amount удаляем незначащие нули, если они есть. Этого требует описание интерфейса.
PHP:
$desc=trim($desc); $pcode=trim($pcode); $amount=floatval($amount);
Получаем подпись XML-пакета с помощью функции _GetSign(). На вход функции подаем строку, полученную в результате склейки параметров, как это предусмотрено в описании интерфейса. Параметры должны склеиваться именно в таком порядке, как это указано ниже. Значения всех параметров при формировании строки подписи обязательно должны быть в кодировке Win1251! Поэтому - внимание! - если функция _WMXML2(), с которой мы сейчас работаем, получила переменные $desс или $pcode в кодировке, отличной от Win1251, то их нужно сперва перекодировать. Остальные параметры содержат всегда только цифры и английские буквы, поэтому для них кодировка никакой роли не играет.
PHP:
$rsign=_GetSign($reqn.$tranid.$purse.$rpurse.$amount.$period.$pcode.$desc.$wminvid);
Теперь преобразуем специальные символы ("<", "&" и др.) в html-сущности. Если этого не сделать, то при попадании в $desc или $pcode таких символов WebMoney наш запрос не примет и вернет ошибку "A name was started with an invalid character". Обратите внимание, что при формировании строки подписи переменные $desc и $pcode пребывали в своем первозданном виде, со всеми спецсимволами, а преобразование мы выполняем уже после получения подписи. Преобразовать спецсимволы в PHP можно с помощью функции htmlspecialchars():
PHP:
$pcode=htmlspecialchars($pcode, ENT_QUOTES);
$desc=htmlspecialchars($desc, ENT_QUOTES);
Но это еще не всё. $desc и $pcode могут содержать русские символы. Например, в $desc может быть такой текст: "тестовый товар & тестовая услуга". И здесь нужно понимать, как подготовить $desc и $pcode для передачи в составе XML-пакета. Дело в том, что содержимое полученного от вас XML сервер WebMoney попытается прочитать так, будто он пришел в кодировке Unicode. Если сервер встретит в пакете русские символы в другой кодировке, то вернет ошибку: "An invalid character was found in text content" ("Обнаружен ошибочный символ"). Для того чтобы этого не произошло, нам нужно принудительно перекодировать $desc и $pcode в UTF-8 и уже в таком виде включать их в XML-запрос.
Сделать преобразование кодировок можно с помощью функции iconv() из одноименного расширения PHP:
PHP:
$pcode=iconv("CP1251", "UTF-8", $pcode);
$desc=iconv("CP1251", "UTF-8", $desc);
То же самое, но с помощью функции mb_convert_encoding() из расширения mbstring:
PHP:
$pcode=mb_convert_encoding($pcode, "UTF-8", "windows-1251");
$desc=mb_convert_encoding($desc, "UTF-8", "windows-1251");
Можно даже преобразовать не в UTF-8, а в html-сущности, это тоже сработает:
PHP:
$pcode=mb_convert_encoding($pcode, "HTML-ENTITIES","windows-1251");
$desc=mb_convert_encoding($desc, "HTML-ENTITIES","windows-1251");
Если же iconv и mbstring вашим сервером не поддерживаются, то могу предложить еще один вариант. Оставьте $desc и $pcode в кодировке Win1251, а в начале XML-запроса вставляйте заголовок <?xml version='1.0' encoding='windows-1251'?>. Он укажет, что данные в пакете переданы в Win1251, и сервер WebMoney обработает их именно в этой кодировке. То есть пакет будет начинаться так:
Код:
$xml=" <?xml version='1.0' encoding='windows-1251'?>
<w3s.request>
...
Правда, хотя на момент написания этих строк такой вариант работал нормально, не могу поручиться, что он будет работать всегда, поскольку точная логика обработки XML-пакета сервером WebMoney не известна, и не ясно, будет ли всегда сервер учитывать заголовок XML-запроса.

Понятно, что если переменные $desc и $pcode были переданы в функцию _WMXML2(), уже пребывая в кодировке UTF-8, то никаких преобразований делать не нужно.

Наконец, формируем XML-пакет с запросом:
PHP:
$xml="
    <w3s.request>
        <reqn>$reqn</reqn>
        <wmid>$Global_WMID</wmid>
        <sign>$rsign</sign>
        <trans>
            <tranid>$tranid</tranid>
            <pursesrc>$purse</pursesrc>
            <pursedest>$rpurse</pursedest>
            <amount>$amount</amount>
            <period>$period</period>
            <desc>$desc</desc>
            <wminvid>$wminvid</wminvid>
            <onlyauth>$onlyauth</onlyauth>
        </trans>
    </w3s.request>";
Отправляем запрос на сервер WebMoney и получаем от него ответ с помощью функции _GetAnswer(). На вход функции подаем URL интерфейса X2 из глобального массива $XML_addr, а также пакет XML-запроса, сформированный только что:
PHP:
$resxml=_GetAnswer($XML_addr[2], $xml);
Для отладки и поиска ошибок может потребоваться прочитать XML-ответ "в чистом виде". Тогда просто раскомментируйте следующую строку:
PHP:
// echo $resxml;
Вызовом функции simplexml_load_string() из библиотеки SimpleXML создаем на основе XML-ответа, полученного от WebMoney, объект. Параметры XML-ответа становятся свойствами объекта, и мы сможем легко получать к ним доступ.
PHP:
$xmlres = simplexml_load_string($resxml);
Если $xmlres не создан, значит, мы по какой-то причине не получили ответ от WebMoney. Тогда прерываем работу функции:
PHP:
if(!$xmlres) {
    $result['retval']=1000;
    $result['retdesc']="Не получен XML-ответ";
    return $result;
}
Читаем следующие свойства объекта: retval (содержит результат выполнения запроса; если перевод на кошелек выполнен успешно, то retval равен 0), retdesc (содержит расшифровку результата), operation/datecrt (содержит дату и время создания транзакции в системе WebMoney в случае успешного перевода, либо дату и время последнего успешного перевода в случае ошибки). Сохраняем эти переменные в массив $result.
PHP:
$result['retval']=strval($xmlres->retval);
$result['retdesc']=iconv("UTF-8", "CP1251", strval($xmlres->retdesc));
$result['date']=strval($xmlres->operation->datecrt);
Обратите внимание, что содержимое поля <retdesc> мы перекодировали из UTF-8 в Win1251. Дело в том, что XML-ответ от WebMoney приходит в кодировке Windows1251, но SimpleXML при помещении XML-данных в объект принудительно превратил их в Юникод. Такая вот у него особенность. А так как <retdesc> - это строка, и теоретически она может содержать русские символы, то при выемке её из объекта мы возвращаем ей "родную" кодировку. Хотя, в общем, это не обязательно и зависит от ваших нужд и задач.

На выходе функция _WMXML2() возвращает массив $result:
PHP:
return $result;
Теперь осталось только проверить, как работает то, что мы написали. Создадим скрипт для тестов test.php:
PHP:
// test.php - скрипт для тестирования
include("wmxml.inc.php");
$tranid="1";
$purse="кошелек_отправитель";
$rpurse="кошелек_получатель";
$amount="0.10";
$period="0";
$pcode="";
$desc="тестовый товар & тестовая услуга";
$wminvid="0";
$onlyauth="1";
$r=_WMXML2($tranid,$purse,$rpurse,$amount,$period,$pcode,$desc,$wminvid,$onlyauth);
echo "Результат (0 - успешно) - ".$r['retval']."<br>";
echo "Расшифровка - ".$r['retdesc']."<br>";
echo "Дата и время - ".$r['date']."<br>";
Этот код должен выполнить перевод 0.10 WM без протекции; с примечанием "тестовый товар & тестовая услуга"; с кошелька_отправителя на кошелек_получателя; при условии, что соблюдены ограничения на прием переводов, установленные получателем (onlyauth=1).

Естественно, на кошельке-отправителе $purse должно быть достаточно средств для совершения перевода.
 
Поддержи проект! Нажми!