Выписка счета в системе WebMoney
Программная выписка счета в система WebMoney или переопределение потоков ВВ дочернего процесса. Александров Константин
🕛 20.10.2006, 11:03
Итак в настоящее время активно растет число интернет-магазинов, причем многие из них продают электронные товары (программы, шаблоны сайтов) или оказывают электронные услуги (ведение статистика сайта, доски объявлений, платные каталоги), да и завести себе свой собственный интернет-магазин не составит труда. И есть тому масса примеров - существует очень много мелких сайтов, продающих может быть только одну программу или электронную книгу. С этого же начинался сайт http://www.zahodi-ka.ru, первоначально созданный мной для продажи за символическую сумму самодельной игрушки. Продажа была организована как у всех подобных проектов - на страничке расписывался сложный процесс оплаты, результатом которого должен был стать лицензионный ключ от игрушки. По показаниям счетчика скачиваний можно было понять, что игрушку активно качают, однако за полгода мне пришло всего одно письмо с подобным содержанием: "Дай, пожалуйста, ключ от игрушки. От тебя ж сильно не убудет, если не получишь лишние 10 рублей". (дословно текст письма не вспомню, но было что-то похожее). Ключ я конечно же дал, коль просят, но вывод для себя сделал, что никто не купил именно потому, что платить просто было лень. А игрушка и сегодня лежит на странице http://www.zahodi-ka.ru/skachat/skachat.shtml, правда уже бесплатно.Итак после этого Вы, надеюсь, поняли всю важность создания удобного пользовательского интерфейса. Этим мы сейчас и займемся, а именно написание программы, автоматизирующей процесс оплаты товара и выдачи его пользователю. Сначала необходимо представить, как будет происходить процесс и какие программа для этого потребуется написать.
Процесс:
1) Пользователь заходит на страницу, вписывает свой WMID (идентификатор, получаемый при регистрации в системе WebMoney), нажимает кнопку "оплатить" и ему приходит счет для оплаты.
2) Пользователь платит по счету и переходит по ссылке, указанной в параметрах счета и получает товар.
Следовательно для этого потребуются 2 программы:
1) Программа обработки данных формы и выписки счета на WMID пользователя.
2) Программа проверки оплаты счета и выдачи товара.
Для выписки счета на 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.
Исходные коды модуля можно скачать, перейдя по ссылке: http://download.webmoney.ru/WMSigner.zip, после чего модуль надо будет скомпилировать под ту операционную систему, на которой работает Ваш сайт. Можно отправить этот архив администрации вашего хостинга и попросить скомпилировать, а можно попытать удачу самому, для этого создадим файл poehali.pl со следующим кодом:
#!/usr/bin/perl system('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 (записать строку, например http://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<br><br><br>"; # вызов модуля WMSigner pipe(IN,OUT); # создание канала связи (пишешь в OUT, читаешь из IN) OUT->autoflush(1); $pid = open(CHILD,"|-"); # разделение на 2 процесса #связывание ввода доч. проц. с дескриптором CHILD if ($pid != 0){ ## если данный поток - родитель close(OUT); # Родителю OUT не нужен, будет писать через CHILD print CHILD $PlanStr."\004\r\n"; # передача параметров потомку close(CHILD); # передача закончена, дескриптор не нужен $SignStr=$_ while (<IN>); # Получение данных от клона close(IN); waitpid($pid,0); }else{ #### если клон(потомок) close(IN); open(STDOUT,">&=OUT"); # замена стандартного вывода на дескр. OUT close(OUT); system('./wmsigner'); # запуск модуля WMSigner (будет возвращать данные в OUT) exit;}; print "Цифровая подпись (возвращена WMSigner\'ом): $SignStr<br><br><br>"; $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 запрос к системе:<br>".$W3sUrl."<br><br><br>"; $ua2 = LWP::UserAgent->new; $res2=$ua2->get($W3sUrl); $buf=$res2->content; print "Ответ системы:<br>".$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 Вы указали пароль не от резервной копии ключей или скопировали не резервную копию ключей.
Вот и все, теперь останется только адаптировать пример под свою задачу, а в следующей статье я расскажу как организовать проверку, был ли оплачен счет.