Система своевременного пополнения мобильного счета


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

    СОДЕРЖАНИЕ


    ОБОЗНАЧЕНИЯ И СОКРАЩЕНИЯ
    ВВЕДЕНИЕ
    1. ОПИСАНИЕ СЕРВИСОВ И СИСТЕМ
    2. ЗАДАЧА ПРОЕКТА
    3. ОПИСАНИЕ ПРОЕКТА
    3.1. Средства для получения состояния лицевого счета
    3.2. Средства для работы с ЯД
    3.3. Средства для работы с Твиттером
    4. РАБОТА И ЛОГИКА
    ЗАКЛЮЧЕНИЕ
    СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ

    ОБОЗНАЧЕНИЯ И СОКРАЩЕНИЯ


    ЛИСА — личный интернет-сервис абонента;
    ЯД — Яндекс.Деньги;
    Выпить ЯДу — получить денежные средства на Яндекс-кошелек;
    БД — база данных.

    ВВЕДЕНИЕ


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

    Хочется отметить, процесс так меня захватил, что теперь не уверен, — сэкономил ли я на всем этом время или проще было оставить все как есть? Однако, человек я такой, увлекаюсь во всякие интересные занятия быстро, поэтому большее удовольствия принес сам процесс реализации, чем конечный результат.

    1. ОПИСАНИЕ СЕРВИСОВ И СИСТЕМ


    ЛИСА— сервис, который позволяет абонентам Компании «Мотив» управлять текущим состоянием своего лицевого счета и пользоваться услугами при помощи сети интернет.

    ЯД — электронная платёжная система. Многие магазины и компании реализуют у себя поддержку ЯД, что позволяет расплачиваться за товары и услуги легко и просто. «Мотив», как раз, одна из тех компаний, где реализована возможность пополнения баланса через ЯД.

    Твиттер — система, позволяющая пользователям отправлять короткие текстовые заметки (до 140 символов), используя веб-интерфейс, SMS, средства мгновенного обмена сообщениями или сторонние программы-клиенты. Отличительной особенностью Твиттера является публичная доступность размещённых сообщений, что роднит его с блогами.

    Вернуться к содержанию →

    2. ЗАДАЧА ПРОЕКТА


    Реализация системы пополнения баланса мобильного телефона с кошелька ЯД является основной задачей проекта, пополнение баланса должно происходить без какой-либо заданной периодичности. Мобильный счет должен пополняться только тогда, когда это необходимо.

    Система должна включать в себя:
    1. Средства для получения состояния лицевого счета;
    2. Средства для работы с ЯД;
    3. Средства для работы с Твиттером.

    Вернуться к содержанию →

    3. ОПИСАНИЕ ПРОЕКТА


    Проект размещен на площадке Мастерхост, хостинг получен бесплатно, в рамках программы поддержки студентов. Потребовалось обратиться в техническую поддержку, для включения curl и получения информации по поводу планировщика, так как на windows-тарифах данная услуга не предоставлялась. Было предложено добавить запись в crontab на unix-сервере, на что я и согласился. Я передал ссылку на скрипт и указал периодичность в 10 минут, после чего меня предупредили, — если скрипт будет создавать большую нагрузку, его придется отключить.

    Вернуться к содержанию →

    3.1. Средства для получения состояния лицевого счета

    В первой версии состояние мобильного счета приходилось получать непосредственно через сайт ЛИСЫ, что занимало достаточно продолжительно время и не всегда давало результат, порой длительность операции достигала 10 секунд, что было просто неприемлемо.

    Рисунок 3.1 — Сайт сервиса ЛИСА.

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

    Рисунок 3.2 — Информация о «Гаджете баланса» и кнопка для его формирования.

    Ранее мне не приходилось работать с гаджетами для Windows 7 или Vista, после установки\переустановки операционной системы я сразу отключал все это дело, так как не видел в этом необходимости, а на домашнем компьютере у меня старая-добрая Windows XP.

    Рисунок 3.3 — Внутреннее строение «Гаджета баланса».

    В каталоге «motiv_balance.gadget\js» находятся файлы:
    • balance.js — основная логика гаджета;
    • jquery.js — распространенная javascript-библиотека;
    • settings.js — реализация функций чтение и сохранение настроек гаджета.
    Я принялся за изучение файла balance.js и сразу наткнулся на искомую функцию getInfo(), в которой формируется и отправляется запрос:
       _req = new ActiveXObject("Microsoft.XMLHTTP");
       /*don't change this sequence! it invokes non-ordering requests: xz why*/
       _req.open("GET", url, true);
    ожидается ответ, далее передается в функцию showResponse(), которая и отвечает за обработку и отображение результата.

    Как вы уже заметили, обращение к сайту оператора представляет собой не что иное, как обычный GET-запрос, который состоит из нескольких параметров:
    • uid — уникальный идентификатор, привязанный к номеру абонента;
    • b — тип запроса данных. Может принимать два значения: «a» — упрощенный отчёт (только состояние лицевого счета) или «b» — расширенный (состояние лицевого счета + остатки по абонементам\пакетам услуг, например, количество купленных SMS или MMS). Не уверен, что всё это работает, данное значение, мистическим образом, зависит от System.Gadget.docked;
    • a — случайное число.
    Вот как это выглядит:
       b = System.Gadget.docked ? 'a' : 'b';
       a = Math.round(Math.random() * 10000);
       /*it should be global: no ideas why*/
       var url = "https://gadget.motivtelecom.ru/?uid=UID&b="+b+"&a=" + a;
    В ответ получаем либо информацию о текущем состоянии счета, например:
       3=RUB:80р.45к.
    где 3 — это номер версии гаджета, на момент написания статьи (var VERSION=3;), либо извещение об ошибке с подробным описанием:
       3=ERR

    Вооружившись этими знаниями, я написал простейшую функцию, которая cURL-ом отправляет запрос.
        $req_lisa = array(
            CURLOPT_HEADER => 0,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_SSL_VERIFYPEER => 0,
            CURLOPT_SSL_VERIFYHOST => 0,
            CURLOPT_RETURNTRANSFER => TRUE,
            CURLOPT_URL => LISA_URL
        );
        $ci = curl_init();
        curl_setopt_array ($ci, $req_lisa);
        $result_lisa = curl_exec($ci);
    Время, требуемое на выполнение запроса и обработку результата, с 10 секунд уменьшилось до 0.26 секунды.

    Вернуться к содержанию →

    3.2. Средства для работы с ЯД

    Для работы с ЯДом требуется:
    1. Изучить документацию;
    2. Зарегистрировать приложение (получить client_id);
    3. Получить временный токен (сроком менее 1 минуты);
    4. Получить токен авторизации (сроком на 1 год).

    Рисунок 3.4 — Информация о зарегистрированном приложении.

    Полученный токен авторизации ограничен 1 магазином (Компания «Мотив») и лимитом «1 тысяча рублей в неделю» с возможностью просмотра информации о текущем счете:
    scope=payment.to-pattern("1122").limit(7,1000) account-info
    Теперь в нашем распоряжении есть методы:
    • account-info — информация о состоянии счета, данный метод мною не использовался;
    • request-payment — запрос платежа;
    • process-payment — подтверждение платежа;
    Для осуществления платежа, мы формируем шаблон платежа:
    pattern_id=1122&PROPERTY1=908&PROPERTY2=9089XXX&sum=%s
    и отправляем request-payment запрос на ЯД, если все верно, то получаем json-строку со значением «success» параметра status и желанный request_id, все остальное для нас, отличное от «success», равносильно неудаче и может зависеть от разных факторов, таких как: технические проблемы на стороне Яндекса, технические проблемы на стороне магазина или что-то еще.
    Если всё в полном порядке, то отправляем уже process-payment запрос, содержащий request_id, тем самым мы подтверждаем платеж:
    
        $yad = json_decode($result, 1);
        if ( $yad['status'] == 'success' ) {
            $pay_fields = sprintf($process_fields, $yad['request_id']);
            curl_setopt($ci, CURLOPT_URL, YM_PROC_URL);
            curl_setopt($ci, CURLOPT_POSTFIELDS, $pay_fields);
            $result_pay = curl_exec($ci);
            curl_close($ci);
            return json_decode($result_pay, 1);
        }
    
    В случае успешного или неудавшегося платежа, мы так же получаем json-строку с параметром status. При значении «success» параметра status в ответ приходит номер платежа и информация о состоянии счета.

    Вернуться к содержанию →

    3.3. Средства для работы с Твиттером

    Для работы была выбрана готовая библиотека, требовалось лишь зарегистрировать свое приложение на сайте для разработчиков и получить все необходимые параметры для работы:
    • Consumer key
    • Consumer secret
    • Access token
    • Access token secret

    Мне показалось, что будет очень уныло и не круто, если в твиттере будут публиковаться одни и те же твиты, поэтому я набросал набор фраз, которыми сейчас оперирует мое приложение-бот:
    function GetRandomTwit() {
        $music_bands = array (
            'Queen', 'Coldplay', 'Muse', 'Justice', 'Duft Punk', 'Radiohead', 
            'Adele', 'Beatles', 'Red Hot Chili Peppers', 'Foo Fighters', 'Killers',
            'Gorillaz' );
        $rand_music_band = $music_bands[rand(0, sizeof($music_bands)-1)];
        $twi_mask = array (
            'На счету Лео, совершенно точно, есть %1$s #motiv #motivtelecom #wtf',
            'У Лео на телефоне: %1$s #motiv',
            'Баланс изменился, теперь составляет %1$s #motiv',
            '%1$s если быть точным #motiv',
            '#motiv хватит списывать деньги! На счету осталось %1$s',
            'Мы принимаем Яндекс.Деньги, вот они %3$s. Остаток: %1$s #motivtelecom',
            '%1$s и постоянно убывает, что делать? #motiv',
            'А баланс-то изменился, теперь там %1$s #motiv',
            'Переслушивал дискографию '.$rand_music_band.' и вдруг... на счету осталось уже %1$s #motiv',
            'БАЦ! И на счету уже %1$s #motiv #motivtelecom #whyyyyyy',
            'Иногда, #motiv списывает деньги просто так, возможно, это происходит прямо сейчас ;) Остаток %2$s руб.',
            'Зуб даю, списали случайно, Лео никуда не звонил! Ну вот, теперь уже %1$s',
            'Я не спец в финансовом деле, но знаю, что на счету Лео %1$s #motivtelecom',
            'Y U SO GREEDY? %2$s rub #motiv #motivtelecom',
            '#motiv у меня ВСЁ записано! Баланс %1$s #motivtelecom #yusogreedy',
            'Если бы знал, что ботам Лео ничего не платит, никогда бы у него не стал работать! Остаток %2$s руб.',
            'Сбор средств на GPRS-трафик ЯД: %3$s. Остаток на счету: %1$s #motiv',
            'Дааа, жаль у тебя не безлимит :( %2$s руб. #motiv #motivtelecom',
            'Я бот-меломан, сейчас слушаю '.$rand_music_band.', а на счету %2$s руб. #motiv #'.str_replace(' ','', strtolower($rand_music_band)),
            'А ведь раньше я работал почтовым роботом.. до чего я докатился! :( А баланс уже %2$s руб.',
            '#motiv дадите спокойно послушать альбом '.$rand_music_band.' или нет? Ок, %2$s руб на счету.',
            'И снова здравствуйте! На счету %1$s #motivtelecom',
            'Отправил, за счет Лео, СМС другу - спам-боту %) Теперь на счету %1$s #motiv',
            'Отправляйте мне свои предложение, в поле Примечание, на ЯД %3$s :) На счету %2$s руб.',
            'Я образованный бот, почему я должен это делать? Ок, Лео, на твоем счету %2$s руб. #motiv',
            'Работа - не волк, поэтому сообщу о балансе в следующий раз. #motiv',
            'BREAKING NEWS! Balance: %2$s rub.',
            'Я свободно владею несколькими языками, а должен заниматься этим! Лео, на твоем счету %2$s руб.'
        );  
        return ($twi_mask[rand(0, sizeof($twi_mask)-1)]);
    }
    Работает библиотека предельно просто:
    $twitter = new Twitter(TWT_CON_KEY, TWT_CON_SEC, TWT_ACC_TOK, TWT_ACC_TOK_SEC);
    $twitter->send(sprintf(GetRandomTwit(), GetSumWithPost($current_bal_value), $current_bal_value, YM_WALLET));

    Вернуться к содержанию →

    4. РАБОТА И ЛОГИКА


    Главная задумка приложения — сделать плату своевременной, мне не нужно, чтобы баланс пополнялся каждый день на 50 или 100 рублей, ведь я могу просто не пользоваться в этот день телефоном и это можно было реализовать на ЯД. Дело оставалось за логикой работы, ведь все компоненты уже есть и работают.

    Опустив лишние проверки, все сводится к понятному виду:
    1. Проверяю текущий баланс, чтобы знать когда пополнять, надо всегда быть в курсе сколько сейчас денег на счету;
    2. Определяю разницу между эталонным значением баланса (100 рублей) и текущим;
    2.1. Разница больше заданного значения (20 рублей):
    2.1.1. произвожу оплату;
    2.1.2. пишу новое значение в БД;
    2.1.3. пишу в Твиттер.
    2.2. Разница меньше заданного значения (20 рублей):
    2.2.1. Сравниваю текущее значение с последним, записанным в БД, если разница положительная, значит баланс пополнился:
    2.2.1.1. пишу новое значение в БД;
    2.2.1.2. пишу в Твиттер, что кто-то из вне пополнил баланс.
    2.2.2. Сравниваю текущее значение с последним, записанным в БД, если разница отрицательная и меньше заданного значения (-5 рублей):
    2.2.2.1. пишу новое значение в БД;
    2.2.2.2. пишу в Твиттер, что баланс изменился.

    Есть некоторые оговорки, которые пришлось учитывать:
    — компания «Мотив» не принимает платежи менее 10 рублей — это означает, что разница между текущим состоянием счета и эталонным заданным значением должна составлять, как минимум, 10 рублей, чтобы произвести пополнение;
    — сумма в 20 рублей взята для того, чтобы часто не писать в Твиттер о пополнении счета;
    — разница в 5 рублей, о которой упоминалось выше, взята не просто так, ранее ограничения не было вообще и приложение сообщало, посредством Твиттер, каждые 10 минут о том, что баланс изменился на 7 копеек, а то и еще меньше — это сподвигло меня поставить некий ограничитель, сначала он был равен 1 рублю, но и этого оказалось мало, теперь ограничитель равен 5 рублям и меня ничто больше не беспокоит;
    — если разница между текущим значением и эталонным оставляет более 100 рублей, то начинаем подозревать что-то неладное и пополняем баланс частями, сначала 100 рублей, потом оставшиеся деньги, по такому же принципу.
    $pay_bot = (floor(BAL_LEVEL-$current_bal_value)>BAL_PAY_LEVEL_MAX)?BAL_PAY_LEVEL_MAX:(floor(BAL_LEVEL-$current_bal_value));
    Будет достаточно времени, чтобы ответить на вопросы: «Куда делись деньги? Почему такой большой минус? Кто виноват?», выпить чашечку чая и, в конце концов, отключить бота.

        $pay_bot = (floor(BAL_LEVEL-$current_bal_value)>BAL_PAY_LEVEL_MAX)?BAL_PAY_LEVEL_MAX:(floor(BAL_LEVEL-$current_bal_value));
        if ( $pay_bot >= BAL_PAY_LEVEL ) {
            $pay_bot += BAL_UP_SUM;
            $ym_pay_state = YandexMoneyPay($pay_bot);
            if ( $ym_pay_state['status'] == YM_PAY_STAT ) {
                $twitter = new Twitter(TWT_CON_KEY, TWT_CON_SEC, TWT_ACC_TOK, TWT_ACC_TOK_SEC);
                $twitter->send(sprintf($plus_pay, GetSumWithPost($pay_bot), 
                    GetSumWithPost($current_bal_value+$pay_bot), GetSumWithPost($ym_pay_state['balance'])));
            }
        } else {
            $pay_user = floor($current_bal_value - $last_bal_value);
            $bal_diff = $last_bal_value - $current_bal_value;
            if ( $pay_user > 0 or $bal_diff >= BAL_CHANGE_LEVEL ) {
                $twitter = new Twitter(TWT_CON_KEY, TWT_CON_SEC, TWT_ACC_TOK, TWT_ACC_TOK_SEC);
                if ( $pay_user > 0 )
                    $twitter->send(sprintf($user_pay, GetSumWithPost($pay_user), GetSumWithPost($current_bal_value)));
                elseif ( $bal_diff >= BAL_CHANGE_LEVEL )
                    $twitter->send(sprintf(GetRandomTwit(), GetSumWithPost($current_bal_value), $current_bal_value, YM_WALLET));
            }
        }
    Отмечу, что на значение BAL_UP_SUM увеличивается любой платеж. Сейчас значение BAL_UP_SUM равно 1, вы видите, что выше все расчеты применяются с округлением в меньшую сторону, чтобы выйти за пределы 99 рублей с копейками следует добавить 1 рубль. Так же это значение можно увеличивать для того, чтобы приложение не докучало своей постоянно писаниной в Твиттер.

    Вернуться к содержанию →

    ЗАКЛЮЧЕНИЕ


    Во время написания столкнулся с некоторыми вопросами, на которые так и не нашел ответ.
    Я долго не мог понять, почему не могу сделать перевод на мобильный счет через ЯД, хотя в ответе на request-payment-запрос приходит значение «success». Оказалось, что приведенный в документации пример:
    pattern_id=2904&phone-prefix=921&phone-number=9538ХХХ&sum=300.00
    с содержанием полей phone-prefix и phone-number, не обязателен для каждого магазина, у Мотива эти параметры носят имена PROPERTY1 и PROPERTY2, у МТС или Мегафон эти поля тоже могут носить другие названия. Поля для Мотива мне подсказали модераторы клуба API ЯД, что и странно. Раз в ЯД знаю значения этих полей, почему просто, во время платежа, не могут переназначить со своих имен параметров на параметры магазина?

    И еще один вопрос, он напрямую связан с первым. Когда я отправлял запрос с неверными именами полей мне, почему-то, приходило значение «success», так же приходил параметр request_id, по которому нужно подтверждать платеж. Такие платежи, конечно, не проходили, но почему в status мне приходил «success»? В руководстве разработчика ясно написано, что «success» — это успешное выполнение.

    Буду рад, если кто-то ответит на эти вопросы.

    Хочется выразить благодарность:
    • Сотруднику компании «Мотив» — Марине Neumann;
    • Модераторам клуба API Яндекс.Денег — Лине и hh;
    • Сотрудникам компании Мастерхост.
    Результат работы: http://twitter.com/001011010

    Вернуться к содержанию →

    СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ


    1. API Яндекс.Денег. Руководство разработчика // материалы сайта api.yandex.ru/money/doc/dg/.
    2. Клуб для разработчиков, использующих API Яндекс.Денег // материалы сайта clubs.ya.ru/moneyapi/.
    3. Twitter. Сайт для разработчиков // материалы сайта dev.twitter.com.
    4. ЛИСА. Гаджет баланса // материалы сайта lisa.motivtelecom.ru.
    5. Материалы сайта ru.wikipedia.org.
    Share post

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 26

      +6
      Слишком много
      Вернуться к содержанию →


      А по существу — идея интересна. Можно развить его дальше — чтобы любой мог автоматически пополнять баланс своего телефона.
        +5
        Я просто запланировал платежи в личном кабинете QIWI и раз в 5 месяцев счёт пополняю.
          +4
          И к тому же, там предлагают записать номер, для экстренного пополнения счета.
          Набрал и +10 руб на счету моментально. Не более 5 раз за день. Полезная штука!
          +4
          Автор знает толк в извращениях :)
          Раз в месяц захожу в Я.Д, нажимаю «повторить платеж» для сотового, тырнета, гор. телефона и все.
            0
            Ну, во-первых, мне теперь вообще не надо ничего нажимать :) Если карту Сбербанка можно будет привязать к ЯД, то можно будет работать и с картой. А во-вторых, опыт работы с ЯД, раньше никогда не работал с их API.
              0
              В сбербанк-онлайн есть автоплатежи, не?
            +11
            Автор не извращенец, а программист :)
              0
              >и вдруг… на счету отсалось
                0
                Точно ведь, не заметил.
                +1
                на что только не пойдут люди из-за несовершенства сервисов:) мой оператор исправно и автоматически снимает с карты деньги как только остаётся на мобильном счёте менее 100 рублей.
                  +1
                  Какой оператор? :)
                    –1
                    мегафон, не сочтите за рекламу
                    0
                    Наш 2 дня нес письмо в тех поддержку, а потом еще неделю думали, что ответить, не думаю, что стоит такого ждать в ближайшее время от них. Тоже интересно, что за оператор.
                    0
                    В сервис гиде мегафона можно if $<x do $+y
                      0
                      спам хештегов #motiv и #motivtelecom? )
                        +7
                        Не хватает обложки и рецензии!
                          +3
                          Сообщения напоминают шутки от Шахиджаняна в соло, извините.
                            +1
                            Сбербанк предлагает сервис автопополнения телефона с карточки при достижении определенного минимума на счету телефона. Включается одной SMS, и больше никогда ничего нажимать не надо.
                              0
                              Хорошо, я посмотрю. Вчера и сегодня в ответ на запрос получаю только:
                              3=ERR
                              Видимо, придется воспользоваться Вашим способом.
                              0
                              а как узнать свой uid?
                                0
                                файл motiv_balance.gadget\js\balance.js, функция getInfo()
                              • UFO just landed and posted this here
                                  0
                                  Можно и так, я не спорю и если Вам этот способ подходит — отлично, а еще можно не заниматься интересным делом, а посмотреть ТВ.
                                  Из предложенных вариантов, больше всего понравился способ ryo_oh_ki. Он ближе всего к тому, что мне нужно.
                                  +2
                                  Боюсь вас расстроить, но у операторов большой тройки подобный сервис уже налажен. В основе лежит идея регистрации карты у оператора и автоматического списывания N рублей при падении баланса ниже M. Платёж можно настроить не только для себя, но и для других абонентов, можно проводить единоразовые платежи. Чтобы с карты не могли списаться все деньги, устанавливаются лимиты списания.

                                  МегаФон
                                  Наиболее гибко автопополнение настраивается через Сервис-Гид. Также доступен по SMS, USSD, IVR.
                                  paycard.megafon.ru

                                  Билайн
                                  Раздел «Автооплата телефона»
                                  pay.beeline.ru

                                  МТС
                                  Год назад было только через Сбербанк, сейчас, добавили общую версию.
                                  autopay.mts.ru

                                  Теле2
                                  Автоматических плетежей до сих пор нет, можно только привязать карту и вручную платьить по USSD/SMS вручную
                                  market.tele2.ru/page/tele2/about/information/
                                    0
                                    Осталось дождаться такого у нашего :)
                                      0
                                      Раньше не знал, настроил, счастлив!

                                    Only users with full accounts can post comments. Log in, please.