Страница 1 из 1

Модуль mysql/Payment - очень неаккуратно...

Добавлено: Ср май 30, 2007 2:38 am
regressor
Поставил я значит абилис... И начал прикручивать к нему шлюз платежной системы киберплат (автоматы приема платежей). Платеж с киберплата это три запроса, подписанных RSA на cgi приложение: 1 - запрос проверки аккаунта, 2 - платеж, 3 - проверка статуса платежа. CGI приложение тоже отвечает подписаными пакетами. Пока проверка статуса платежа не вернет успех будет проходить попытка проведения платежа.

В таблице платежей меня все поля устроили, в поле опис я добавлял номера диллера/точки и т.п. - примерно 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;
}
при этом $cip - ip адрес с которого пришел платеж (смотрел на отладке - ошибка 3 есть, адрес при этом заполнен т.е. проблема не в нем - пробовал и 127.0.0.1)

Результат ошибки был весьма веселый - в функции add в оригинальной версии модуля запрос
"INSERT INTO payments" на ошибки не проверяется вообще и сразу за ним идет запрос на добавление денег на счет, который проходит без проблем:

Код: Выделить всё

$Bill->info( { BILL_ID => $user->{BILL_ID} } );
$Bill->action('add', $user->{BILL_ID}, $DATA{SUM});
if($Bill->{errno}) { return $self; }
Т.е. в результате имеем увеличение счета пользователя и отсутствия платежа в таблице. В итоге автомат приходит проверить статус - не находит его, решает что платеж не добавлен и начинает все сначала :) Таким образом юзер кладет 10 рублей а на счет падает 50-80... Пришлось в модуле все переделать - добавление платежа и обновление счета пользователя сделать в транзакции благо mysql innodb это позволяет.

Хотелось бы пожелать уважаемому автору, написавшему довольно неплохую систему биллинга все-таки не оставлять без проверки ошибок код, работающий с деньгами. И если какие-то операции требуют несколько последовательных операций с базой делать это все в транзакции и в случае ошибки откатывать всю транзакцию + выдавать ошибку. Если же платеж уже проведен, а добавить его в базу не получается по каким-то причинам его надо складывать в отельный каталог в виде файлов для последующей ручной проверки.

Примерный вариант работы в транзакции (begin_work временно отключает авто-коммит и запускает транзакцию):

Код: Выделить всё

my $sql = Abills::SQL->connect(...);
my $db = $sql->{db};
$rc  = $db->begin_work;

.....

if (error) {
   $db->rollback;
} else {
   $db->commit;
}
P.S. Абиллс я ставил из портов (0.35) и при этом обнаружил кое-где забытые включенные функции отладки: например при логине в пользовательский интефейс слева внизу есть написанная белым шрифтом на белом фоне надпись debug и если на нее навести мышкой можно подсмотреть пароль пользователя. Модуль сообщений выводит SQL запросы при удалении сообщения. Может и еще где есть.

Добавлено: Ср май 30, 2007 7:38 am
~AsmodeuS~
уже довольно давно в ЦВС слеждующий код

Код: Выделить всё

  if ($user->{BILL_ID} > 0) {
    if ($DATA{ER} != 1) {
      $DATA{SUM} = $DATA{SUM} / $DATA{ER} if (defined($DATA{ER}));
     }

    $Bill->info( { BILL_ID => $user->{BILL_ID} } );
    $Bill->action('add', $user->{BILL_ID}, $DATA{SUM});
    if($Bill->{errno}) {
       return $self;
      }
    
    $self->query($db, "INSERT INTO payments (uid, bill_id, date, sum, dsc, ip, last_deposit, aid, method, ext_id) 
           values ('$user->{UID}', '$user->{BILL_ID}', now(), '$DATA{SUM}', '$DATA{DESCRIBE}', INET_ATON('$admin->{SESSION_IP}'), '$Bill->{DEPOSIT}', '$admin->{AID}', '$DATA{METHOD}', '$DATA{EXT_ID}');", 'do');
  }
Для интерграции с внешними ситемами біл разработан модуль Paysys (комерческий)

Добавлено: Чт май 31, 2007 1:38 am
regressor
В том коде, что вы привели я транзакции не вижу и в данной ситуации возможен вариант когда платеж клиенту упадет а в таблице платежей он будет отсутствовать. Да еще и ошибка вернется при этом. Если ваш платный модуль написан в том же стиле то что-то желания нет его покупать. Кроме того мне проще свой написать и проитись по системе с напильником что я и сделал.

А вот отладочную информацию лучше все-таки отключать... Не дело это когда на компьютере пользователя в кэше браузера лежит файл с его паролем.

Добавлено: Чт май 31, 2007 7:57 am
~AsmodeuS~
спасибо я учту все пожелания