Сбербанк разработал собственную систему для работы с ПУ (платежное устройство) используя API на стороне клиента (оператора связи).
Основное это perl-скрипт, с помощью которого осуществляется взаимодействие.
Алгоритм:
- Абонент вводит лицевой счет (логин).
- Сбербанк запрашивает ФИО и необходимую сумму платежа.
- Сервер выдает информацию по логину.
- Сбербанк формирует форму с данными.
- Абонент подтверждает платеж.
- Сбербанк передает данные о платеже.
- Скрипт обрабатывает платеж, вносит информацию в БД.
Скрипт:
#!/usr/bin/perl use lib '/usr/local/billing/payments'; use strict; use CGI; use LB; use POSIX; use Time::localtime; use Time::Local; use DBI; use utf8; ############## ! fix me ! ############### my $lbcore_host = 'localhost'; # IP сервера LBcore my $manager_login = 'sberbank'; # логин менеджера АСР my $manager_pass = 'XXXXXXXXXXXXXXXXXXXXXXXXXXX'; # пароль менеджера АСР my $type = TYPE_USER_LOGIN; # Тип идентификатора ######################################### # Список примерных расшифровок ответов для некоторых кодов возврата my %result_messages = ( 0 => 'OK', 1 => 'Временная ошибка. Повторите запрос позже.', 4 => 'Неверный формат идентификатора абонента', 5 => 'Идентификатор абонента не найден', 7 => 'Прием платежа запрещен провайдером', 8 => 'Прием платежа запрещен по техническим причинам', 79 => 'Счет абонента не активен', 241 => 'Сумма слишком мала', 242 => 'Сумма слишком велика', 243 => 'Невозможно проверить состояние счета', 300 => 'Ошибка провайдера' ); my $balance; my $login; # Некоторые входные параметры скрипта my $command = CGI::param ( 'command' ) || ''; # Тип запроса my $account = CGI::param ( 'account' ) || ''; # Номер абонента my $txn_id = CGI::param ( 'txn_id' ) || ''; # Номер платежа (уник. для внешней системы) my $amount = CGI::param ( 'sum' ) || ''; # Сумма платежа # Информация, возвращаемая скриптом my $result_code = 0; # Код ответа my $prv_txn = ''; # Номер платежа в системе провайдера #------------------------------------------------------------------------------- if($command ne 'check_balance') { if (($command eq '') || ($account eq '') || ($txn_id eq '') || ($amount eq '')) { print_response($amount, 300); exit; } } #------------------------------------------------------------------------------- my $pay = new LB(host => $lbcore_host, user => $manager_login, pass => $manager_pass); my $r; if ( $command eq 'check' || $command eq 'check_balance') { # Тип запроса - проверка номера абонента для платежа #if ( $account !~ /^\d+$/ ) #{ # Неверное значение № абонента (синтаксис) # $result_code = 4; #} #else #{ $pay->connect || tmp_error(); $result_code = 300; #($r, $balance) = $pay->check( 'number' => $account, 'type' => $type, 'fetch_balance' => 'yes' ); ($r, $balance, $login) = $pay->check( 'number' => $account, 'type' => $type, 'fetch_balance' => 'yes', 'fetch_param' => TYPE_FIO); $result_code = 0 if $r == 0; $result_code = 243 if $r == -2; $result_code = 5 if $r == 2; $result_code = 1 if $r == 11; $pay->disconnect; #} } elsif ( $command eq 'pay' ) { # Тип запроса - проведение платежа my $date = CGI::param ( 'txn_date' ); # Дата и время платежа (внешней системы) #if ( $account !~ /^\d+$/ ) #{ # Неверное значение № абонента (синтаксис) $result_code = 4; #} #elsif ( $amount !~ /^\d+\.\d{0,2}$/ ) if ( $amount !~ /^\d+\.\d{0,2}$/ ) { # Неверное значение суммы (синтаксис) $result_code = 7; } elsif ( $amount <=0 ) { $result_code = 241; } elsif ( $txn_id !~ /^\d+$/ ) { # Неверное значение номера платежа (синтаксис) $result_code = 7; } else { my $timestamp; my $amountcurr; if ($date =~ /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/) { $result_code = 300; $pay->connect || tmp_error(); $timestamp = mktime($6,$5,$4,$3,$2-1,$1-1900,0,0,-1); ( $r, $prv_txn, $amountcurr ) = $pay->payment ( 'number' => $account, 'type' => $type, 'amount' => $amount, 'receipt' => $txn_id, 'date' => $timestamp, ); $amount = $amountcurr if $r == 12; $result_code = 0 if (($r == 0)||($r == 12)); $result_code = 243 if $r == -2; $result_code = 5 if $r == 2; $result_code = 1 if $r == 11; $result_code = 1 if !defined($prv_txn) || ($prv_txn eq ''); $pay->disconnect; } else { # Неверное значение даты (синтаксис) $result_code = 7; } } } else { # Неизвестный тип запроса $result_code = 300; } my $vnesti=&test($account); print_response($amount,$result_code, $balance,$login,$vnesti); exit; ################################################################################ sub print_response { my ($amount,$result,$balance,$login,$vnesti) = @_; my $msg = $result_messages{$result}; print "Content-type: text/xml\n\n"; print "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; print "<response>\n"; if ( $command ne 'check_balance' ) { print "<osmp_txn_id>$txn_id</osmp_txn_id>\n"; } if ( $command eq 'pay') { print "<prv_txn>$prv_txn</prv_txn>\n"; print "<sum>$amount</sum>\n"; } if ( $command eq 'check_balance' ) { print "<login>$login</login>\n"; print "<curr_balance>$balance</curr_balance>\n"; print "<vnesti>$vnesti</vnesti>\n"; } print "<result>$result</result>\n"; print "<comment>$msg</comment>\n"; print "</response>\n"; } ################################################################################ sub tmp_error { print_response('',1); die("Can't connect to LBcore\n"); } sub test () { my $dToDay = localtime(time); my $sNextMon = ($dToDay->year()+int(($dToDay->mon+1)/12) + 1900) . ((($dToDay->mon+1)%12)+1) . "01"; my $db_server = 'localhost'; my $db_login = 'login'; my $db_pass = 'Pa55W0rD'; my $db_name = 'billing'; my($dbh,$query,$result); $dbh = DBI->connect('DBI:mysql:database='.$db_name.';host='.$db_server, $db_login, $db_pass) || print('Не могу подключиться к серверу баз данных: '.$DBI::errstr).die(); $query = ("SELECT ifnull(CEIL( t.rent - ag.balance + ifnull( z.`above` , 0 )),0) AS vnesti FROM `vgroups` AS v LEFT JOIN (SELECT s.`vg_id` , serc.`above` , serc.`descr` FROM `services` AS s, service_categories AS serc WHERE s.`need_calc`=1 AND s.`tar_id` = serc.`tar_id` AND s.`serv_cat_idx` = serc.`serv_cat_idx` )z ON v.`vg_id` = z.`vg_id` LEFT JOIN accounts AS a ON a.uid = v.uid LEFT JOIN accounts_addons_vals AS f ON a.uid = f.uid LEFT JOIN agreements AS ag ON ag.uid = v.uid AND ag.agrm_id = v.agrm_id LEFT JOIN tarifs AS t ON t.tar_id = v.tar_id WHERE a.login='$account' AND balance < rent AND v.archive <>1 AND v.`vg_id` NOT IN (SELECT `vg_id` FROM `tarifs_rasp` WHERE `change_time`=$sNextMon) UNION SELECT ifnull(CEIL( q.rent - ag.balance + ifnull( z.`above` , 0 )),0) AS vnesti FROM `vgroups` AS v LEFT JOIN (SELECT s.`vg_id` , serc.`above` , serc.`descr` FROM `services` AS s, service_categories AS serc WHERE s.`need_calc`=1 AND s.`tar_id` = serc.`tar_id` AND s.`serv_cat_idx` = serc.`serv_cat_idx` )z ON v.`vg_id` = z.`vg_id` LEFT JOIN accounts AS a ON a.uid = v.uid LEFT JOIN accounts_addons_vals AS f ON a.uid = f.uid LEFT JOIN agreements AS ag ON ag.uid = v.uid AND ag.agrm_id = v.agrm_id LEFT JOIN (SELECT tr.`vg_id`,tr.`tar_id_new`, t.descr, t.rent,tr.`change_time` FROM `tarifs_rasp` as tr, tarifs as t WHERE tr.`tar_id_new`=t.tar_id and tr.`change_time`=$sNextMon )q ON v.`vg_id` = q.`vg_id` WHERE a.login='$account' AND balance < rent AND v.archive <>1 AND q.`change_time`=$sNextMon ORDER BY 1"); $result = $dbh->prepare($query); $result->execute() || print('Не могу выполнить запрос ('.$query.'): '.$DBI::errstr).die(); $a=($result->fetchrow_array()); if ($a == undef) {$a=0;} $dbh->disconnect(); return $a; }
Данный скрипт работает с LANBilling версии 2.0 сборка № 18. К данному скрипту также нужны библиотеки.
Софт установлен в каталог
/usr/local/billing/
Соответственно туда же ссылаются
use lib '/usr/local/billing/payments';
Также используются другие необходимые модули, которые должны быть установлены на сервере, и библиотеки:
use strict; use CGI; use LB; use POSIX; use Time::localtime; use Time::Local; use DBI; use utf8;
LB.pm
Парсер реестра платежей из формата Сбербанка в формат АСР LANBilling
Вообще-то скрипт на перловке а не на пхп и терзают смутные сомнения, подойдет ли он для Сербанка т.к. ежу понятно, что вы просто перезапостили его откуда-то
Спасибо за комментарий.
Исправил на perl — действительно )
Скрипт сам сбер предложил в качестве протокола взаимодействия. Мы его только подогнали под себя.
Добавил библиотеку и парсер под статьей.
По большому счету оставил здесь этот скрипт для себя. Самое важное там — запрос к БД, который определяет по лицевому счету ФИО и сумму НЕОБХОДИМОГО ПЛАТЕЖА. Сумма необходимого платежа считает так: сумма абонентской платы (с учетом смены тарифа и перерасчета) + сумма аренды за доп. услуги (статический IP или разовые услуги, которые были оказаны в прошедшем расчетном периоде) — сумма текущего баланса.
Этот же запрос используется для скрипта, который рассылает всем абонентам напоминание о необходимости пополнить баланс ЛС до 1 числа следующего расчетного периода, чтобы услуги продолжили предоставляться без перерыва.