Как я ЖКХ-платежи автоматизировал

37c7f5e1907d0e5d0ca70d6a132125d3.JPG

Если у вас есть возможность оплачивать все коммунальные счета из одного приложения, я вам завидую. А уж если у вас стоят умные счётчики, то вы просто счастливчик.

У меня вот две обслуживающие организации, и заведены два личных кабинета на разных сайтах. В одном кабинете я должен ежемесячно передавать показания всех счётчиков и оплачивать воду, электричество и прочие коммунальные услуги. А в другом я должен продублировать показания счётчиков горячей воды и заплатить за тепло. Дни, когда можно передавать показания по воде и электричеству, разные. А передавать показания и потом платить после выставления счёта лучше вовремя, иначе возникает неразбериха. Можно, конечно, ставить напоминалки, но это не слишком-то помогает.

Решил я немного упростить жизнь и написать программку, которая будет оповещать меня о наступивших событиях, таких как необходимость заплатить или передать показания, и позволит передавать показания счётчиков сразу на оба сайта в нужное время.

Под катом рассказ о том, что у меня из этого получилось.

Кабинеты, о которых я говорил, выглядят следующим образом. Это кабинет МосОблЕИРЦ. Здесь я ввожу данные четырёх счётчиков воды (два на кухне и два в санузле), электросчётчика и оплачиваю коммунальные счета. Как видите, ребята ещё немножко и на рекламе зарабатывают.

image-loader.svg

Кабинет теплоэнергетической компании Глобус буйством красок напоминает сайты девяностых годов, тогда они почти все были такие.

image-loader.svg

Здесь я должен продублировать показания двух счётчиков горячей воды и заплатить за тепло.

Ну хорошо, так какой быть программе? Это может быть сайт. Можно задуматься о мобильном приложении. Но мне ближе всего C#. Стало быть, создаём проект Windows Forms .NET.

image-loader.svg

Идея такая. Программа взаимодействует с сайтами посредством HTTP-запросов. Если в одном из кабинетов выставляется счёт, появляется строка типа «Выставлен счёт на 1234,00 руб». Если счёта в текущем месяце ещё нет, будет написано «Оплачено». Линки на кабинеты МосОблЕирц и Глобус открывают браузер, после чего там можно будет совершить платёж. Счётчики воды можно оплачивать с 5 числа, электросчётчик — с 15. Если в текущем месяце показания ещё не были переданы, соответствующие текстбоксы включаются, и показания можно передавать. Справа от текстовых полей находятся прежние показания.

Программа запускается при запуске Windows. Если всё оплачено, и показания отправлены, программа тихо закрывает себя, иначе пользователь может выполнить необходимые действия. Поскольку я всегда выключаю настольный компьютер на ночь, а включаю почти каждый день, это идеальный вариант.

Ну что ж, приступим к исследованию сайта МосОблЕирц (на самом деле, mosenergosbyt.ru). Сначала нам нужно пройти аутентификацию. Открываем Fiddler, вводим в браузере учётные данные и жмём «Войти».

image-loader.svg

Смотрим полученные запросы в фидлере. Туннели мы игнорируем, обращения к Яндексу — тоже, это, скорее всего, сбор статистики. Будем смотреть запросы на хост my.mosenergosbyt.ru.

Итак, первый запрос, который нас интересует — это запрос на аутентификацию. Ищем. Ага, вот:

POST https://my.mosenergosbyt.ru/gate_lkcomu? action=auth&query=login HTTP/1.1

Это то, что нам надо. В Cookie видим слова Bitrix, значит это PHP. Взглянув на тело запроса, видим, что логин и пароль передаются в явном виде.

Ну что ж, попробуем для начала создать минимальный запрос с телом, содержащим логин и пароль. Смотрим в отладчике, что происходит.

Так, код ответа — 200 OK. Хорошо. Но что это? JSON ответа содержит текст

Уважаемый пользователь, с Вашего IP-адреса была зафиксирована вредоносная активность при обращении к Единому Личному кабинету АО Мосэнергосбыт и ООО МосОблЕИРЦ. В связи с этим доступ с Вашего IP-адреса был временно заблокирован. Для разблокировки доступа или если ваш адрес был заблокирован по ошибке, просим Вас обратиться в службу поддержки пользователей по телефону +7 (499) 550–9–550. Мы заботимся о безопасности Ваших данных и благодарим за понимание!

Вот те и на! Надо было создавать тестовый кабинет… Ладно, попробуем пока сделать более полный запрос, в котором пропишем значения хедеров и добавим Cookie, как в оригинальном запросе. Вот таким образом.

Посылаем… И получаем JSON с текстом «Ошибок нет»:

{
  "success": true,
  "total": 1,
  "data": [
    {
      "kd_result": 0,
      "nm_result": "Ошибок нет",
      "id_profile": "218e52f3-4014-4e45-8ac8-56cbafb006f6",
      "cnt_auth": 0,
      "new_token": null,
      "session": "8IM3EOS5RHETWFMUFXHFEUNECMZMGJJ8S162DD04"
    }
  ],
  "metaData": {
    "responseTime": 0.031
  }
}

Ура! Зря пугали, выходит. Просто этот сайт не принимает запросы без cookies. Полученный JSON содержит хеш сессии, который будет использоваться в URL последующих запросов.

Дальше всё идёт по накатанной колее. Конструируем запрос, получаем JSON, десериализуем. Надо только учесть, что некоторые запросы требуют указания идентификаторов абонента и учётной записи. Чтобы получить их, придётся добавить соответствующий метод.

Создаём запрос для получения текущего баланса, запрос для получения данных счётчиков. Никаких проблем, всё работает.

Теперь попробуем сделать обновление показаний счётчиков. В кабинете показания передаются по одному:

image-loader.svg

Значит, так и сделаем соответствующий метод.

Работает. Замечательно! Ну, и в конце надо реализовать возможность выхода из кабинета для порядка.

Отлично, с первым кабинетом разобрались. Теперь приступим к кабинету компании Глобус. Вот аутентифицирующий запрос:

POST https://lk.globusenergo.ru/ajax/auth.php HTTP/1.1

В теле запроса также передаются учётные данные в незашифрованном виде. Смотрим cookies:

image-loader.svg

Снова битрикс. Памятуя прошлый опыт, надо будет не забыть указать cookie в нашем запросе. А что это за PHPSESSID, откуда он берётся? Так, ага… Чтобы получить это значение, нужно перед аутентификацией сделать GET-запрос на lk.globusenergo.ru, и оно будет передано в хедере Set-Cookie. Ладно, так и сделаем.

Запускаем отладчик… и что там у нас в ответе? А в ответе — html личного кабинета. Значит, аутентификация прошла.

Об автоматическом перенаправлении

Интересно то, что запрос для аутентификации возвращает HTTP-код 302, и HttpHandler автоматически делает перенаправление и выполняет второй запрос GET по адресу из хедера Location, Таким образом, после вызова HttpClient.SendAsync мы видим два запроса в фидлере — POST и GET. Возвращаемое значение при этом содержит ответ второго запроса с html. Если бы мы захотели отказаться от автоматического перенаправления, нам следовало бы создавать объект HttpClient посредством конструктора, принимающего объект HttpClientHandler со свойством AllowAutoRedirect = false.

Из html мы можем сразу получить текущий баланс. Фрагмент личного кабинета в браузере

image-loader.svg

соответствует вот этому фрагменту html:


Ищем в тексте html слово «DEBT_END» и находим в JS-коде большую переменную arrayResultForJs, которая, помимо прочего, содержит следующее:

"DEBT":{"DEBT_END":"0.00","~DEBT_END":"0.00"}

Это нам и нужно для получения баланса. Как получить это значение из html? Можно воспользоваться библиотекой HtmlAgilityPack, как это описано на stackoverflow, но к чему такие сложности для решения простой задачи? Просто воспользуемся регулярными выражениями.

Последнее, что надо сделать — это возможность передачи новых показаний. В кабинете показания двух счётчиков передаются в одном запросе:

image-loader.svg

Пробуем написать соответствующий код, и… ничего не получается. В ответе содержится html главной страницы, а значения счётчиков не обновляются. Попытка, ещё одна… Нет, не работает. Хорошо, а если запустить запрос из закладки фидлера Composer? Вроде, проходит… Тогда сравним текст запроса из программы и реального запроса. Ага, вот: запросы отличаются значением sessid в теле запроса. А откуда взять это значение? Оказывается, первый html после авторизации содержит определение bitrix_sessid в JS-коде. Нужно получить это значение из html и прописать его в запросе. Пробуем, и… всё в порядке!

Ну вот и всё. Программа готова. Казалось бы, не бог весть какая автоматизация, но оказалось, что это довольно-таки удобно: всё необходимое сосредоточено в одном месте, не нужно дублировать показания счётчиков на разных сайтах. Кроме того, программа служит напоминалкой, и теперь я не забываю совершить необходимые действия.

Единственный недостаток — в кабинете Глобуса сумма к оплате продолжает висеть несколько дней даже после совершения платежа. Уж не знаю, то ли платёж должен поступить на счёт, то ли они вообще вручную подтверждают внесённую сумму, но приводит это к тому, что программа не закрывается при запуске и каждый день сообщает о том, что счёт выставлен. Возможно, придётся с этим что-то делать…

Конечно, очень бы хотелось иметь умные счётчики, чтобы передавать показания на серверы автоматически, а не лазить по углам с фонарикам, записывая цифры на бумажку. В следующий раз, когда настанет время менять счётчики, обязательно задумаюсь об этом.

→ Полный код проекта на Github

Спасибо за внимание!

© Habrahabr.ru