вторник, 22 июля 2008 г.

Программная выписка счета в системе WebMoney

Процесс: * Пользователь заходит на страницу, вписывает свой WMID (идентификатор, получаемый при регистрации в системе WebMoney), нажимает кнопку "оплатить" и ему приходит счет для оплаты. * Пользователь платит по счету и переходит по ссылке, указанной в параметрах счета и получает товар.
Следовательно для этого потребуются 2 программы: * Программа обработки данных формы и выписки счета на WMID пользователя. * Программа проверки оплаты счета и выдачи товара.
Для выписки счета на WMID пользователя необходимо выполнить следующий https запрос: https://w3s.webmoney.ru/asp/Invoice.asp?SL=LoginOfStores& SP=PurseOfStores& CL=LoginOfCust& IN=OrderID& D=Desc& AD=InvAddress& A=Amount& E=Experation& P=Period& RN=RequestN& SS=SignStr (эта инофрмация приведена на сайте системы WebMoney, однако если бы задача решалась так просто, я бы не написал эту статью). Теперь про каждый параметр подробнее:


SL=LoginOfStores - WM-идентификатор web-ресурса (просто регистрируете нового пользователя в системе WebMoney для приема платежей с сайта и указываете его WMID);SP=PurseOfStores - кошелек Web-ресурса (номер кошелька, на который будут совершаться платежи - кошелек пользователя с WMID из первого пункта);CL=LoginOfCust - WM-идентификатор покупателя (этот параметр указывает пользователь, программа получает данные из формы);IN=OrderID - номер счета (номер заказа) на Web-ресурсе (в магазине). Номер генерируется программой (1) самостоятельно и хранится в базе Web-ресурса (магазина, сервиса) - по этому номеру в дальнейшем будет определятся состояние счета (оплачен или нет). (номер должен быть целым числом);D=Desc - описание товара, за который выписывается счет. Параметр должен быть строкой от 0 до 255 символов;AD=InvAddress - адрес доставки товара. Параметр должен быть строкой от 0 до 255 символов (сюда будем писать ссылку, по которой пользователь сможет получить товар. Это будет ссылка на программу (2), которой будет передаваться параметр IN=OrderID);A=Amount - сумма счета. Данный параметр формируется программой (1). Параметр должен быть числом с плавающей точкой (разделитель - точка);E=Experation - срок действия счета в днях (целое число от 0 до 255);P=Period - срок действия протекции сделки. Параметр формируется программой (1). (максимальный срок протекции сделки при оплате счета, если указать здесь 0, то счет не будет иметь срока протекции);RN=RequestN - этот параметр является уникальным возрастающим числом. Параметр формируется программой (1) и должен быть всегда больше такого же параметра, указанного в предыдущем запросе. Лучше всего формировать этот параметр из текущего года, месяца, дня, часа, минуты, секунды и сотых секунд. (должен быть целочисленным 16-ти разрядным значением);SS=SignStr - цифровая подписью запроса. Он гарантирует то, что именно ваш Web-ресурс выписал счет.

При подготовке https запроса Основной проблемой является формирование именно SS=SignStr. Сначала должна быть сформирована строка PlanStr как сумма параметров OrderID, LoginOfCust, PurseOfStores, Amount, Desc, InvAddress, Period, Experation, RequestN, описанных выше и точно в таком же порядке. После этого необходимо из строки подписи PlanStr сделать строку цифровой подписи SignStr, для этого используется специальный модуль WMSigner, которому на вход подается строка PlanStr, а он возвращает SignStr.
Исходные коды модуля можно скачать, перейдя по ссылке: go.php?url=download.webmoney.ru/WMSigner.zip, после чего модуль надо будет скомпилировать под ту операционную систему, на которой работает Ваш сайт. Можно отправить этот архив администрации вашего хостинга и попросить скомпилировать, а можно попытать удачу самому, для этого создадим файл poehali.pl со следующим кодом:

#!/usr/bin/perlsystem('g++ cmdbase.cpp crypto.cpp md4.cpp rsalib1.cpp signer.cpp wmsigner.cpp');
print "Content-type:text/html\n\n";print "И все-таки она вертится!";


Теперь создадим папку (ну, например wm) в корневом каталоге сайта и скопируем туда наш скрипт и содержимое архива WMSigner. Права доступа к нашему скрипту необходимо поставить 711. Теперь из строки браузера следует запустить скрипт poehali.pl (записать строку, например go.php?url=www.zahodi-ka.ru/wm/poehali.pl) и подождать ответа скрипта. Если на сервере есть компилятор G++ и необходимые модуля, то в папке wm после запуска окажется файл a.out, который нужно переименовать в wmsigner (маленькими буквами).
Когда модуль уже готов, можно приступить к написанию программы (1), которая и будет его вызывать. В документации на сайте системы WebMoney вызов осуществляется с помощью функции open2, однако такой вариант работает не всегда, поэтому мы будет вызывать с помощью функции system. Создадим программу (назовем ее wschet.pl):

#!/usr/bin/perl
sub dopprobelz # функция дополнения строки нулями вначале{my($str)=@_[0];my($len)=@_[1]; my $delta=$len-length($str);my $hwost='';for (my($a)=0; $a<$delta; $a++) {$hwost=$hwost.'0';};$str=$hwost.$str; return $str;};# подключение модулейuse FileHandle;use IPC::Open2;use CGI;use Socket;use LWP::UserAgent;
# задание параметров запроса# сюда укажите свои данные$OrderID = '999'; # номер счета на Вашем сайте$PurseOfStores ='Z406593169293'; # кошелек сайта$LoginOfStores = '321373645120'; # WMID сайта$LoginOfCust = '083872782699'; # WMID покупателя, зададим как константу$InvAddress = 'INVADDRESS'; # адрес$Amount = '10'; #сумма счета, которую должен оплатить покупатель$Desc = 'DESC'; # описание$Period = '0'; #Срок протекции сделки$Experation = '3'; #Срок действия счета - 3 дня
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time());$RequestN=(1900+$year).dopprobelz($mon, 2);$RequestN=$RequestN.dopprobelz($mday, 2).dopprobelz($hour, 2);$RequestN=$RequestN.dopprobelz($min, 2).dopprobelz($sec, 2).dopprobelz($sec, 2);
$PlanStr="$OrderID$LoginOfCust$PurseOfStores$Amount";$PlanStr=$PlanStr."$Desc$InvAddress$Period$Experation$RequestN";
print "Content-Type: text/html\n\n";
print "Строка подписи \$PlanStr: $PlanStr


";
# вызов модуля WMSigner
pipe(IN,OUT); # создание канала связи (пишешь в OUT, читаешь из IN)OUT->autoflush(1);$pid = open(CHILD,"-"); # разделение на 2 процесса#связывание ввода доч. проц. с дескриптором CHILDif ($pid != 0){ ## если данный поток - родительclose(OUT); # Родителю OUT не нужен, будет писать через CHILDprint CHILD $PlanStr."\004\r\n"; # передача параметров потомкуclose(CHILD); # передача закончена, дескриптор не нужен$SignStr=$_ while (); # Получение данных от клонаclose(IN);waitpid($pid,0);}else{ #### если клон(потомок)close(IN);open(STDOUT,">&=OUT"); # замена стандартного вывода на дескр. OUTclose(OUT);system('./wmsigner'); # запуск модуля WMSigner (будет возвращать данные в OUT)exit;};
print "Цифровая подпись (возвращена WMSigner'ом): $SignStr


";
$W3sUrl="
https://w3s.webmoney.ru/asp/Invoice.asp?SL=$LoginOfStores&";$W3sUrl=$W3sUrl."SP=$PurseOfStores&CL=$LoginOfCust&IN=$OrderID&D=$Desc&";$W3sUrl=$W3sUrl."AD=$InvAddress&A=$Amount&E=$Experation&P=$Period&";$W3sUrl=$W3sUrl."RN=$RequestN&SS=$SignStr";
print "HTTPS запрос к системе:
".$W3sUrl."


";
$ua2 = LWP::UserAgent->new; $res2=$ua2->get($W3sUrl);$buf=$res2->content;
print "Ответ системы:
".$buf; # Далее необходимо произвести разбор буфера и запись счета в базу данных магазина


К этой программе следует положить в папку скомпилированный модуль WMSigner(права доступа 711), резервную копию ключей (при запуске Keeper запросит создать резервную копию кючей и задать пароль для этой копии, после создания скопируйте файл *.kwm на сайт к программе wschet.pl и переименуйте его в keys.kwm, выставьте ему права доступа 440) и файл wmsigner.ini (права доступа 440) следующего содержания

WMID
pass.
/keys.kwm

Где WMID - идентификатор магазина, pass - пароль от резервной копии ключей, ./keys.kwm - путь к файлу ключей.
Теперь детально разберем способ запуска модуля wmsigner из нашей программы. Для вызова модуля используется функция system(), которая связывает потоки ввода-вывода вызываемого процесса с потоками ВВ вызывающей процесс программы, то есть данные, переданные вызывающей программе извне будет получать и запущенный функцией system() процесс и данные, возвращаемые процессом будут выводиться туда же, куда и данные вызывающей программы (в нашем случае в браузер пользователя, что нежелательно). Для разделения потоков ВВ наша программа сначала создает клона, потом этот клон вызывает процесс WMSigner и получает от него данные, а основная программа получает данные уже от своего клона, процесс создания клона и переопределения его стандартного дескриптора ввода на CHILD реализован с помощью функции $pid = open(CHILD,"-").
Если модуль выдаст ошибку -3, знайте, что в wmsigner.ini Вы указали пароль не от резервной копии ключей или скопировали не резервную копию ключей.

Комментариев нет: