Создание дополнительных модулей платежных систем
Материал из ISPWiki
В интерфейсе BILLmanager работа с платежными системами отражена в двух местах:
1. На уровне провайдера - Компании->Принимаемые методы оплаты;
2. На уровне клиента - Платежи;
Чтобы создать свой платежный интерфейс, необходимо:
- создать программу или скрипт, который будет заполнять и обрабатывать поля в формах, а также в случае необходимости инициировать оплату;
- создать xml-документ с описанием элементов интерфейса и текстовыми сообщениями на разных языках;
- в случае необходимости создать cgi скрипты или программы, которые будут обмениваться данными с платежными шлюзами.
Содержание |
XML-документ
XML-документ должен располагаться в папке /usr/local/ispmgr/etc и называться billmgr_mod_xxx.xml, где вместо xxx вы можете указать любое название, например, test (billmgr_mod_test.xml). Документ обязательно должен быть в кодировке UTF-8 и иметь следующий вид:
<?xml version="1.0" encoding="UTF-8"?>
<mgrdata>
<metadata name="company.paymethod.edit_XXX" type="form">
<form>
<field name="name">
<input type="text" name="name" empty="no"/>
</field>
<field name="minamount">
<input type="text" name="minamount"/>
</field>
----поля необходимые вам----
</form>
<jscript>
document.frm.func.value = 'company.paymethod.edit';
</jscript>
</metadata>
<metadata name="credit.pay.XXX" type="form">
<form>
<field name="amount">
<input type="text" name="amount" readonly="yes"/>
</field>
----поля необходимые вам----
<field name="merchantfield"> Если используется переход на сайт платежной системы
<link name="merchant"/>
</field>
</form>
<jscript> Если используется переход на сайт платежной системы
function GoMerchant(id, lnk) {
window.open('/mancgi/testcgi?elid='+id);
if (lnk) {
lnk.className = 'disabled';
lnk.onclick = function () { return false; }
}
}
</jscript>
</metadata>
<lang name="en">
<messages name="credit.pay.XXX">
<msg name="amount">Amount</msg>
<msg name="merchant">Pay now</msg>
<msg name="title">XXX pay method</msg>
<msg name="hint_default">Fill form</msg>
<msg name="hint_amount">Amount</msg>
---остальные сообщения----
</messages>
</lang>
</mgrdata>
XXX - это внутреннее имя платежной системы. Обязательно создавайте описание сообщений для английского языка, иначе могут не создаться и остальные языки. Также обратите внимание на поле merchant, оно необходимо, если платежная система использует переход на их сайт.
Программа обработчик
Программа или скрипт должны располагаться в каталоге /usr/local/ispmgr/sbin/. Название файла должно начинаться с символов pm. Набор символов, следующий после pm, будет внутренним названием платежной системы, и элементы интерфейса будут использовать именно это название. Программа должна отвечать основному параметру - при запуске с параметром feature она должна выдавать список возможностей, которые она поддерживает, перечисленные через пробел.
Список возможностей
pmtune Программа будет изменять форму редактирования метода оплаты. Используется для того, чтобы заполнить селекты значениями. BILLmanager передает на стандартный ввод XML-документ, описывающий форму, а потом ждет, когда программа на стандартный вывод подаст модифицированный XML-документ.
pmvalidate Программа будет проверять введенные пользователем значения. Используется для того, чтобы пользователь не мог ввести некорректные значения. BILLmanager передает на стандартный ввод XML-документ, содержащий поля формы с введенными значениями. Если название полей будет param1, param2 b param3, XML будет иметь следующий вид:
<?xml version="1.0" encoding="UTF-8"?> <doc> <param1>value</param1> <param2>value</param2> <param3>value</param3> </doc>
Если все параметры имеют корректные значения, программа должна подать на стандартный вывод pmok, либо в случае ошибки Error: имя_параметра. В случае успеха BILLmanager запишет параметры в таком же XML-виде в таблицу companycrtype в поле params. В дальнейшем при создании платежей вы можете использовать эти данные.
crtune Программа будет изменять форму создания платежа. Используется, чтобы заполнить селекты. Работает аналогично pmtune.
crvalidate Программа будет проверять введенные пользователем значения при совершении платежа. Работает аналогично pmvalidate.
crget Программа будет заполнять поля формы создания платежа. Необходимо, чтобы заполнить поля формы редактирования платежа значениями из базы. Работает аналогично pmtune.
crset Используется, если пользователь не должен переходить на сайт платежной системы, и платеж совершается на основе введенных данных. В BILLmanager данный метод используется при выписке счета WebMoney и оплате через Ogone. Реализовано на основе системы LongTask. В случае ошибки сообщение об ошибке должно быть в следующем формате:
crid код_платежа описание_ошибки
Пользователь увидит желтый баннер с тестом:
Во время совершения платежа произошла ошибка. Код: 'код_платежа' Error: 'сообщение об ошибке'. Исправьте некорректные данные, либо обратитесь в техническую поддержку.
Зачислить платеж можно запустив
/usr/local/ispmgr/sbin/mgrctl -m billmgr credit.setpaid elid=код_платежа info="данные которые вы хотите сохранить"
Cgi программа
Если понадобится обмен данными с платежной системой, нужно будет создавать cgi программы, которые должны располагаться в /usr/local/ispmgr/cgi Https адрес будет - https://your_domain/mancgi/XXX, где XXX - название файла. Примеров cgi программы мы приводить не будем, на просторах интернета хватает примеров, в том числе реализующих общение с платежными шлюзами.
Структура базы данных
Перечислим таблицы, которые могут понадобиться для создания модуля:
1. companycrtype - в этой таблице содержатся данные о методе оплаты. Столбцы:
1.1 id - код метода оплаты;
1.2 currency - валюта. Ссылка на табличку currency;
1.3 minamount - минимальная сумма платежа;
1.4 params - XML-документ с параметрами, введенными на форме;
2. credit - таблица с платежами. Столбцы:
2.1 id - код платежа;
2.2 nativeamount - сумма в валюте метода оплаты;
2.3 state - Статус платежа;
2.4 userinfo - XML-документ с введенными на форме данными;
2.5 info - Используется для сохранения данных о платеже; к примеру, данные присылаемые платежным шлюзом.
Это не полное описание таблиц. В этих таблицах гораздо больше столбцов, но они служебные, и вряд ли понадобятся для платежного шлюза.
Пример скрипта на языке perl
В примере, приведенном ниже, описаны тестовые функции, которые могут понадобиться при создании платежного интерфейса. Для работы понадобятся модули XML::LibXML и Mysql. Название файла - pmtest. Этот пример не реализует какого-либо протокола. Он только показывает какие возможности можно реализовать, используя нашу систему.
Краткое описание примера:
Основная часть служит для того, чтобы определить как запущен процесс, как LongTask или нет, и в зависимости от этого по разному запускаются функции.
pmtune - показывает, как можно заполнить select значениями.
pmvalidate - проверяет, чтобы testinput содержал "test", иначе возвращает ошибку.
crtune - делает то же, что и pmtune.
crget - берет из базы значения и заполняет ими поля в форме.
Указываем интерпретатор скрипта:
#!/usr/bin/perl no warnings;
Подключаем необходимые библиотеки:
use XML::LibXML; use Mysql; use Env;
Открываем файл лога для добавления записей:
open(DEBUG, ">>/tmp/debug.log");
Устанавливаем флаг ошибка (изменяем на 1 в случае возникновения ошибки в работе модуля):
$errflg = 0;
Обрабатываем команду переданную модулю:
foreach $x(@ARGV) {
if ($x eq "pmtune"){
pmtune();
}
if ($x eq "feature"){
print "pmtune pmvalidate crtune crget crset";
}
if ($x eq "pmvalidate"){
pmvalidate();
}
if ($x eq "crtune"){
crtune();
}
if ($x eq "crget"){
crget();
}
if ($x eq "crset"){
crset();
}
}
Обработка завершения модуля:
if ($env{"MGR_LT_PID"}) {
if ($errflg) {
system "/usr/local/ispmgr/sbin/mgrctl -m billmgr longtask.finish elid=".$env{"MGR_LT_PID"}." status=ok";
} else {
system "/usr/local/ispmgr/sbin/mgrctl -m billmgr longtask.finish elid=".$env{"MGR_LT_PID"}." status=err errmsg='Error message'";
}
}
Закрываем лог файл:
close(DEBUG);
Возвращаем XML файл переданный на стандартный ввод:
sub GetInputXml(){
my $input;
my $buf = <STDIN>;
while($buf) {
$input = "$input$buf";
$buf = <STDIN>;
}
return $input;
}
Проверяем переданные значения:
sub pmvalidate{
my $parser = XML::LibXML->new();
my $xml = $parser->parse_string(GetInputXml());
@testinput = $xml->getElementsByTagName('testinput');
print DEBUG $testinput[0]->textContent;
if($testinput[0]->textContent eq "test"){
print "pmok";
} else {
print "Error: testinput";
}
}
Получаем XML формы редактирования метода оплаты со стандартного ввода, подставляем значения и возвращаем XML файл формы:
sub pmtune{
my $input = GetInputXml;
my $parser = XML::LibXML->new();
my $xml = $parser->parse_string($input);
my $root = $xml->getDocumentElement();
my $elem2 = $xml->createElement("slist");
$elem2->setAttributeNode($xml->createAttribute("name","testselect"));
my $slist1=$xml->createElement("msg");
$slist1->appendText( "select_1");
$elem2->appendChild($slist1);
my $slist2=$xml->createElement("msg");
$slist2->appendText( "select_2");
$elem2->appendChild($slist2);
my $slist3=$xml->createElement("msg");
$slist3->appendText( "select_3");
$elem2->appendChild($slist3);
$root->appendChild($elem2);
#print DEBUG $root->toString;
print $root->toString;
}
Получаем XML формы создания платежа со стандартного ввода, изменяем и возвращаем XML файл формы:
sub crtune{
my $input = GetInputXml;
my $parser = XML::LibXML->new();
my $xml = $parser->parse_string($input);
my $root = $xml->getDocumentElement();
my $elem2 = $xml->createElement("slist");
$elem2->setAttributeNode($xml->createAttribute("name","testselect"));
my $slist1=$xml->createElement("msg");
$slist1->appendText( "select_1");
$elem2->appendChild($slist1);
my $slist2=$xml->createElement("msg");
$slist2->appendText( "select_2");
$elem2->appendChild($slist2);
my $slist3=$xml->createElement("msg");
$slist3->appendText( "select_3");
$elem2->appendChild($slist3);
$root->appendChild($elem2);
#print DEBUG $root->toString;
print $root->toString;
}
Получаем XML формы создания платежа со стандартного ввода, заполняем значениями из базы и возвращаем XML файл формы:
sub crget{
my $input = GetInputXml;
my $parser = XML::LibXML->new();
my $xml = $parser->parse_string($input);
my $root = $xml->getDocumentElement();
my @xelid = $xml->getElementsByTagName('elid');
#print DEBUG $xelid[0]->textContent;
my $elid = $xelid[0]->textContent;
$user = "root";
$password = "1";
$database = "billmgr";
$host = "localhost";
my $query="select cr.nativeamount, cr.state, cu.iso, type.params from credit cr \
join companycrtype type on type.id=cr.type join currency cu on cu.id=type.currency where cr.id=$elid";
$dbh = Mysql->Connect($host,$database,$user,$password);
$sth = $dbh->Query($query);
@arr = $sth->FetchRow;
my $elem = $xml->createElement("amount");
$elem->appendText("$arr[0] $arr[2]");
$root->appendChild($elem);
if($arr[1] eq "4"){
$elem = $xml->createElement("merchant");
$elem->appendText("GoMerchant($elid, this); return false;");
$root->appendChild($elem);
} else {
#$root->removeChild($xml->findnodes("//field/merchantfield"));
}
print $root->toString;
}
Обработчик платежа (в случае обработки модулем):
sub crset(){
print DEBUG "\nin crset\n";
print DEBUG "credit id: $ltparams[2]";
return "crid $ltparams[2] Test error";
}
Пример XML-документа
<?xml version="1.0" encoding="UTF-8"?>
<mgrdata>
<metadata name="company.paymethod.edit_test" type="form">
<form>
<field name="name">
<input type="text" name="name" empty="no"/>
</field>
<field name="minamount">
<input type="text" name="minamount"/>
</field>
<field name="testselect">
<select name="testselect"/>
</field>
<field name="testinput">
<input type="text" name="testinput"/>
</field>
</form>
<jscript>
document.frm.func.value = 'company.paymethod.edit';
</jscript>
</metadata>
<metadata name="credit.pay.test" type="form">
<form>
<field name="amount">
<input type="text" name="amount" readonly="yes"/>
</field>
<field name="testselect">
<select name="testselect"/>
</field>
<field name="merchantfield">
<link name="merchant"/>
</field>
</form>
<jscript>
function GoMerchant(id, lnk) {
window.open('/mancgi/testcgi?elid='+id);
if (lnk) {
lnk.className = 'disabled';
lnk.onclick = function () { return false; }
}
}
</jscript>
</metadata>
<lang name="en">
<messages name="credit.pay.test">
<msg name="amount">Amount</msg>
<msg name="merchant">pay now</msg>
<msg name="testselect">Testselect</msg>
<msg name="title">Test pay method</msg>
<msg name="hint_default">Fill form</msg>
<msg name="hint_amount">Amount</msg>
</messages>
</lang>
</mgrdata>
