Поиск по блогу

четверг, 23 октября 2014 г.

Подключаем WalletOne - Единая касса к своему сайту

Во внедрении Единой кассы на свой сайт нет ничего сложного - все доступно и понятно расписано в документации. Даже примеры есть.
Однако мне пришлось столкнуться с некоторыми подводными камнями о чем и хочу поделитья.


Документация

 Первый подводный камень (для разработчика) это получение собственно документации по API. Просто так Вам ее никто не даст. Необходимо регистрировать свой магазин, либо брать учетные данные заказчика и входить под его логином. Даже если Вы являетесь любимым клиентом - пользователем кошелька Единой кассы - вам сие секретное издание будет недоступно!
Как будто у нас владельцы успешных интернет-магазинов как правило являются успешными программистами! Кстати, эту порочную практику поддерживают практически все платежные системы.
Так я вам скажу: если бы было что прятать!!!! (эту фразу надо произносить с одесским акцентом) :)
Но об этом далее

Собственно код

То что код морально устарел - можно даже не говорить, коль используется кодировка Windows-1251.
Используется - да и ладно бы, код то работает и всем хорошо! Но, код ведь внедряемый!
Т.е. ваш сайт/магазин использует UTF-8 (а вы попробуйте сейчас найти сайт с кодировкой CP-1251), а кусочек кода, который необходимо внедрить в него в другой кодировке!
А что такого, скажете Вы. Есть замечательная функция конвертирования iconv() и дело в шляпе.
Но ведь эту функцию еще надо внедрять в уже существующий код. Т.е. разбираться с кодом, пробовать, а тренировочная площадка как правило тоже не предоставляется!
Достаем обшарпанный бубен и настраиваем его на ноту ля, что, напоминаю, 440 герц ...
 Надо отдать должное владельцам Единой кассы - они нашли возможность заплатить денег программеру, который внедрил костыль в виде iconv() (что не скажешь о платежной системе Z-Payment, например).
Почему костыль? Да потому что нужно было внедрять конвертацию на стороне сервера, а еще лучше переписать свою систему под современную кодировку! Неужели это сложно? Любой начинающий программер сделает это ...
Я думаю, все знают что такое функция iconv() и как она работает! Более глючной функции не найти во всем PHP! Об этом говорит хотябы тот факт, что во время всемирного перехода со стандарта Win-1251 на UTF-8 у этой функции было очень много заменителей! Просто наберите в  Google "замена iconv()" и убедитесь сами.
Вот и я нарвался на эти грабли.
Такой код используется в примере в документации:

$value = iconv("utf-8", "windows-1251", $value);
 
а вот часть формы, которая приходит с сервера, где у меня отказывалась работать iconv()

<tr>
<td class='label'>WMI_PAYMENT_NO:</td>
<td class='value'><input type="text" readonly name="WMI_PAYMENT_NO" value="9144"/></td>
</tr>
 
а это ошибка

Notice: iconv() [function.iconv]: Detected an illegal character in input string 

Цифры!! Это цифры illegal character ... !!!
У iconv() есть свои костыли //IGNORE и //TRANSLIT. Понятное дело, что использование этих костылей по отдельности и даже вместе ни к чему хорошему не привели.
Ведь не забывайте, что результат конвертирования хешируется вместе с другими переменными. И в случае с малейшими неточностями в пропадании символа, не той его кодировки или замене на другой (translit) мы ни за что не получим правильный хешкод, который надо сравнивать с хешкодом пришедшим с сервера.

Заменитель iconv()

Данные присылаются с сервера, т.е. на них я повлиять не могу. Выход один: своя функция iconv().
Заменителей iconv() есть великое множество и все их можно разделить на три части: маленькая, средняя и полная - по размеру кода и количеству заменяемых символов. Кроме того есть "математическая" замена, а есть табличная - тупым перебором нахождения символа в таблице в кодировке CP-1251 на соответствующий символ в таблице в кодировке UTF-8.
Из опыта - перебор работает всегда правильно и не глючит. Еще одно преимущество - эти функции двусторонние, т.е. перевод из CP-1251 в UTF-8 и наоборот. Достаточно поменять местами таблицы :)
Я применил "среднюю" функцию - ее оказалось достаточно:

/** Замена функции iconv()
* @param $txt
* @param string $dir
* @return mixed
*/
private function win2utf( $txt, $dir = "wu" )
{
 // cp1251
 $in_arr = array (
  chr( 208 ), chr( 192 ), chr( 193 ), chr( 194 ),
  chr( 195 ), chr( 196 ), chr( 197 ), chr( 168 ),
  chr( 198 ), chr( 199 ), chr( 200 ), chr( 201 ),
  chr( 202 ), chr( 203 ), chr( 204 ), chr( 205 ),
  chr( 206 ), chr( 207 ), chr( 209 ), chr( 210 ),
  chr( 211 ), chr( 212 ), chr( 213 ), chr( 214 ),
  chr( 215 ), chr( 216 ), chr( 217 ), chr( 218 ),
  chr( 219 ), chr( 220 ), chr( 221 ), chr( 222 ),
  chr( 223 ), chr( 224 ), chr( 225 ), chr( 226 ),
  chr( 227 ), chr( 228 ), chr( 229 ), chr( 184 ),
  chr( 230 ), chr( 231 ), chr( 232 ), chr( 233 ),
  chr( 234 ), chr( 235 ), chr( 236 ), chr( 237 ),
  chr( 238 ), chr( 239 ), chr( 240 ), chr( 241 ),
  chr( 242 ), chr( 243 ), chr( 244 ), chr( 245 ),
  chr( 246 ), chr( 247 ), chr( 248 ), chr( 249 ),
  chr( 250 ), chr( 251 ), chr( 252 ), chr( 253 ),
  chr( 254 ), chr( 255 )
 );

 // utf-8
 $out_arr = array (
  chr( 208 ) . chr( 160 ), chr( 208 ) . chr( 144 ), chr( 208 ) . chr( 145 ),
  chr( 208 ) . chr( 146 ), chr( 208 ) . chr( 147 ), chr( 208 ) . chr( 148 ),
  chr( 208 ) . chr( 149 ), chr( 208 ) . chr( 129 ), chr( 208 ) . chr( 150 ),
  chr( 208 ) . chr( 151 ), chr( 208 ) . chr( 152 ), chr( 208 ) . chr( 153 ),
  chr( 208 ) . chr( 154 ), chr( 208 ) . chr( 155 ), chr( 208 ) . chr( 156 ),
  chr( 208 ) . chr( 157 ), chr( 208 ) . chr( 158 ), chr( 208 ) . chr( 159 ),
  chr( 208 ) . chr( 161 ), chr( 208 ) . chr( 162 ), chr( 208 ) . chr( 163 ),
  chr( 208 ) . chr( 164 ), chr( 208 ) . chr( 165 ), chr( 208 ) . chr( 166 ),
  chr( 208 ) . chr( 167 ), chr( 208 ) . chr( 168 ), chr( 208 ) . chr( 169 ),
  chr( 208 ) . chr( 170 ), chr( 208 ) . chr( 171 ), chr( 208 ) . chr( 172 ),
  chr( 208 ) . chr( 173 ), chr( 208 ) . chr( 174 ), chr( 208 ) . chr( 175 ),
  chr( 208 ) . chr( 176 ), chr( 208 ) . chr( 177 ), chr( 208 ) . chr( 178 ),
  chr( 208 ) . chr( 179 ), chr( 208 ) . chr( 180 ), chr( 208 ) . chr( 181 ),
  chr( 209 ) . chr( 145 ), chr( 208 ) . chr( 182 ), chr( 208 ) . chr( 183 ),
  chr( 208 ) . chr( 184 ), chr( 208 ) . chr( 185 ), chr( 208 ) . chr( 186 ),
  chr( 208 ) . chr( 187 ), chr( 208 ) . chr( 188 ), chr( 208 ) . chr( 189 ),
  chr( 208 ) . chr( 190 ), chr( 208 ) . chr( 191 ), chr( 209 ) . chr( 128 ),
  chr( 209 ) . chr( 129 ), chr( 209 ) . chr( 130 ), chr( 209 ) . chr( 131 ),
  chr( 209 ) . chr( 132 ), chr( 209 ) . chr( 133 ), chr( 209 ) . chr( 134 ),
  chr( 209 ) . chr( 135 ), chr( 209 ) . chr( 136 ), chr( 209 ) . chr( 137 ),
  chr( 209 ) . chr( 138 ), chr( 209 ) . chr( 139 ), chr( 209 ) . chr( 140 ),
  chr( 209 ) . chr( 141 ), chr( 209 ) . chr( 142 ), chr( 209 ) . chr( 143 )
 );
 if ( $dir == 'wu' )
  $txt = str_replace( $in_arr, $out_arr, $txt );
 else
  $txt = str_replace( $out_arr, $in_arr, $txt );
 return $txt;
}

Автора кода, к сожалению, наверное уже не найти, потому как код неоднократно подвергался перепосту ... в любом случае большая благодарность ему за потраченное в свое время время ... Я лишь немного доработал его код "под себя" ...
Переключение направления кодировки (хоть в нашем случае это и не потребуется) осуществляется переменной $dir. Значение "wu" - с Win-1251 в UTF-8. Любое другое - обратно.
Осталось дело за малым: заменить в коде все упоминания

$value = iconv("utf-8", "windows-1251", $value);

на

$value = $this->win2utf( $value, "uw" );

И это еще не все!!! (© Реклама)

Я не хочу сказать ничего плохого про своего коллегу - скорее всего как это часто бывает ему так поставили задачу. Программисты народ точный и щепетильный: как написано - так и сделано, буква в букву, пиксель в пиксель!
Вот вы мне ответьте на простой вопрос: если платеж прошел, с сервера пришло уведомление на адрес
https:/myshop.ru/w1/paid.php и ваш код ответил

// TODO: Пометить заказ, как «Оплаченный» в системе учета магазина
print_answer("Ok", "Заказ #" . $_POST["WMI_PAYMENT_NO"] . " оплачен!");
как того требует документация - зачем сервер повторно отсылает такое же сообщение на адрес, что вы указали в настройках на сервере Единой кассы?
Какой в этом сакральный смысл? Ваш код уже отработал, он не ждет ничего больше и в итоге конечно же отправляет ошибку на сервер
// Случилось что-то странное, пришло неизвестное состояние заказа
print_answer("Retry", "Неверное состояние ". $_POST["WMI_ORDER_STATE"]);
Сервер же, добрая душа, будет теперь долбить ваш магазин каждую минуту пытаясь получить ответ OK! А если ошибка была еще и в paid.php - то с удвоенным старанием.

Решение простое: прежде чем обработать запрос, проверяем запись в базе. И, если платежу уже присвоен соответствующий статус - смело отсылаем
print_answer("Ok", "Сообщение принято")
приставучему серверу ... слово "працивный" можно не добавлять
Всем удачи и хорошего дня

PS
Ради интереса/смеха (кому что нравится) решил я  написать в грозную Техподдержку:
Здравствуйте, Уважаемая поддержка!
Я - разработчик, сейчас настраиваю модуль оплаты интернет-магазина http://zzzzzz 
Возникла незадача: сервер ругается на функцию iconv() iconv: Detected an illegal character in input string 
В качестве обработчика формы я использую рекомендуемый вами код, что в документации. Я пробовал испрользовать известные "костыли" типа //IGNORE и //TRANSLIT, а также "исключение вывода ошибки" используя "@iconv()" - ничего не помогло. Конечно же в этом случае получаю ошибку несовпадения хешкода ... 
 Анализ показал, что функции iconv() "не нравится" вот этот код в возвращаемой вашим сервером форме: 
--------------
<tr>
<td class='label'>WMI_PAYMENT_NO:</td>
<td class='value'><input type="text" readonly name="WMI_PAYMENT_NO" value="9144"/></td>
</tr>
-------------- 
Если эту часть кода исключить - форма прекрасно обрабатывается. (Код формы см. во вложении) я прекрасно понимаю, что цифры никак не могут быть "illegal characters" и (думаю, так же как и Вы) знаком с известными глюками стандартной функции iconv(). 
Однако задачу надо решить! Что Вы можете предложить в качестве решения данной проблемы? 
Спасибо
 -- С уважением,

 На что получаю ожидаемый ответ: 

 Добрый день. 
 В данном случае, мы не можем предложить вариант решения данной проблемы. Рекомендуем обратиться к квалифицированным специалистам (веб-разработчикам,программистам). 

Вот так вот ... и даже не подписались :) Т.е. они отказываются сопровождать свой же код и отсылают к сторонним программерам.
Возникает простой вопрос: а что же они тогда поддерживают? :)

Комментариев нет :

Отправить комментарий

Есть что сказать - скажи