Модуль mysql/Payment - очень неаккуратно...
Добавлено: Ср май 30, 2007 2:38 am
Поставил я значит абилис... И начал прикручивать к нему шлюз платежной системы киберплат (автоматы приема платежей). Платеж с киберплата это три запроса, подписанных RSA на cgi приложение: 1 - запрос проверки аккаунта, 2 - платеж, 3 - проверка статуса платежа. CGI приложение тоже отвечает подписаными пакетами. Пока проверка статуса платежа не вернет успех будет проходить попытка проведения платежа.
В таблице платежей меня все поля устроили, в поле опис я добавлял номера диллера/точки и т.п. - примерно 20 символов. В поле ext_id - номер сессии (20 символов). Первое что обнаружилось - поле ext_id - в базе имеет ограничение 16 символов. Этого очень мало. Пришлось добавить. Первая проверка наличия платежа/аккаунта/счета заработала без проблем. Второй пакет с платежом начал как-то странно глючить. Временами функция $payments->add возвращала ошибку 3 (SQL error) ругаясь что поле ip не может быть NULL.
Код следующий:
при этом $cip - ip адрес с которого пришел платеж (смотрел на отладке - ошибка 3 есть, адрес при этом заполнен т.е. проблема не в нем - пробовал и 127.0.0.1)
Результат ошибки был весьма веселый - в функции add в оригинальной версии модуля запрос
"INSERT INTO payments" на ошибки не проверяется вообще и сразу за ним идет запрос на добавление денег на счет, который проходит без проблем:
Т.е. в результате имеем увеличение счета пользователя и отсутствия платежа в таблице. В итоге автомат приходит проверить статус - не находит его, решает что платеж не добавлен и начинает все сначала
Таким образом юзер кладет 10 рублей а на счет падает 50-80... Пришлось в модуле все переделать - добавление платежа и обновление счета пользователя сделать в транзакции благо mysql innodb это позволяет.
Хотелось бы пожелать уважаемому автору, написавшему довольно неплохую систему биллинга все-таки не оставлять без проверки ошибок код, работающий с деньгами. И если какие-то операции требуют несколько последовательных операций с базой делать это все в транзакции и в случае ошибки откатывать всю транзакцию + выдавать ошибку. Если же платеж уже проведен, а добавить его в базу не получается по каким-то причинам его надо складывать в отельный каталог в виде файлов для последующей ручной проверки.
Примерный вариант работы в транзакции (begin_work временно отключает авто-коммит и запускает транзакцию):
P.S. Абиллс я ставил из портов (0.35) и при этом обнаружил кое-где забытые включенные функции отладки: например при логине в пользовательский интефейс слева внизу есть написанная белым шрифтом на белом фоне надпись debug и если на нее навести мышкой можно подсмотреть пароль пользователя. Модуль сообщений выводит SQL запросы при удалении сообщения. Может и еще где есть.
В таблице платежей меня все поля устроили, в поле опис я добавлял номера диллера/точки и т.п. - примерно 20 символов. В поле ext_id - номер сессии (20 символов). Первое что обнаружилось - поле ext_id - в базе имеет ограничение 16 символов. Этого очень мало. Пришлось добавить. Первая проверка наличия платежа/аккаунта/счета заработала без проблем. Второй пакет с платежом начал как-то странно глючить. Временами функция $payments->add возвращала ошибку 3 (SQL error) ругаясь что поле ip не может быть NULL.
Код следующий:
Код: Выделить всё
my $sql = Abills::SQL->connect($conf{dbtype}, $conf{dbhost}, $conf{dbname}, $conf{dbuser}, $conf{dbpasswd});
my $db = $sql->{db};
my $admin = Admins->new($db, \%conf);
$admin->info($conf{SYSTEM_ADMIN_ID}, { IP => $cip});
$payments->addcplat($user, $cip, {SUM => $info{AMOUNT},
DESCRIBE => "$info{AP}/$info{OP}/$info{SD}",
METHOD => '1',
EXT_ID => "$info{SESSION}",
ER => 1 });
if ($payments->{errno})
{
my $dberr = DBI::errstr;
syslog('info', "Error: pay:$cip:$info{SESSION} -> AErr=($payments->{errno}), $dberr / $cip");
$info{ERRMSG} = "ERROR ICODE $payments->{errno}";
$info{error} = 30;
}
Результат ошибки был весьма веселый - в функции add в оригинальной версии модуля запрос
"INSERT INTO payments" на ошибки не проверяется вообще и сразу за ним идет запрос на добавление денег на счет, который проходит без проблем:
Код: Выделить всё
$Bill->info( { BILL_ID => $user->{BILL_ID} } );
$Bill->action('add', $user->{BILL_ID}, $DATA{SUM});
if($Bill->{errno}) { return $self; }

Хотелось бы пожелать уважаемому автору, написавшему довольно неплохую систему биллинга все-таки не оставлять без проверки ошибок код, работающий с деньгами. И если какие-то операции требуют несколько последовательных операций с базой делать это все в транзакции и в случае ошибки откатывать всю транзакцию + выдавать ошибку. Если же платеж уже проведен, а добавить его в базу не получается по каким-то причинам его надо складывать в отельный каталог в виде файлов для последующей ручной проверки.
Примерный вариант работы в транзакции (begin_work временно отключает авто-коммит и запускает транзакцию):
Код: Выделить всё
my $sql = Abills::SQL->connect(...);
my $db = $sql->{db};
$rc = $db->begin_work;
.....
if (error) {
$db->rollback;
} else {
$db->commit;
}