Проверяем валидность внутриигровых покупок через Google API

    Проблема и решение


    Я думаю многие знают что такое Freedom. Для тех кто не знает — это приложение под Android, позволяющее делать внутриигровые покупки в других приложениях без траты денег.

    Описание:
    Вам надоели предложения в играх и программах что-то купить(расширения, монетки и т.п.)? Покупайте их бесплатно с помощью Freedom!
    Пользоваться очень просто: устанавливаете, запускаете (первый запуск может занять какое-то время — до нескольких минут), тапаете по приложению — оно запускается. После этого оно возможно пройдёт проверку лицензии, а при покупке из приложения Вы сможете расплачиваться фэйковой карточкой.

    Ссылка на статью автора: habrahabr.ru/post/163547

    Я до определенной поры игнорировал этот прискорбный факт, тем более что я в какой-то степени согласен с автором, что те кто пользуются этим софтом и так врядли бы купили что-то у меня в игре. Но новый проект над которым мы работаем должен стать мультиплеерным. А это значит, что читеры получат реальное преимущество перед обычными игроками, что меня не особо радует.
    Покурив вечером мануалы по Google API я решил соорудить собственную проверку валидности покупок. Т.к. мы используем Unity3D + Prime31 IAP, пришлось их немного обработать напильником, чтобы они начали выдавать purchase token.

    Для меня архитектура этого дела выглядит вот так:



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

    Алгоритм такой:
    1. Клиент совершает покупку и получает токен
    2. Клиент делает запрос на веб сервер его игры, передавая токен и тип покупки
    3. Веб сервер игры делает запрос к серверу лицензий
    4. Сервер лицензий делает запрос к Google API
    5. В случае успешного ответа, веб сервер игры начисляет игровую валюту и отправляет клиенту результат


    Техническая часть


    Про веб сервер игры я писать не буду, так как это выходит за рамки этой статьи.
    Сервер лицензий написан на Django. Для тех кто желает написать велосипед самостоятельно — в конце статьи есть куски кода на питоне.

    GitHub source: github.com/Agasper/django-google-play-check-payment

    Установка

    Создаем виртуальное окружение, клонируем проект и устанавливаем зависимости

    virtualenv .
    git clone https://github.com/Agasper/django-google-play-check-payment.git project
    source ./bin/activate
    pip install -r ./project/req.txt
    


    Создаем аккаунт и генерируем ключи

    1. Идем на console.developers.google.com/project
    2. Создаем новый проект, например «Payment check»
    3. Переходим в Project -> APIS & AUTH -> APIs и включаем Google Play Android Developer API
    4. Идем в Project -> APIS & AUTH -> Credentials, нажимаем Create new Client ID и создаем новый Service Account
    5. Запоминаем e-mail, генерируем и скачиваем новый P12 ключ
    6. Переименуйте его в key.p12 и положите в директорию keys в корне проекта
    7. Перейдите в play.google.com/apps/publish раздел Настройки -> Аккаунты и права доступа
    8. Нажмите Пригласить пользователя и введите e-mail сервисного аккаунта, который вы создали в пункте 4-5
    9. Дайте этому пользователю права Финансы


    Проверка

    Все что написано ниже запускается в тестовом режиме, если вы хотите использовать Django в боевых условиях, то есть отдельные статьи как это настроить. Например вот: habrahabr.ru/post/226419

    ./manage.py runserver 0.0.0.0:8000
    

    Пример запроса:

    http://host:8000/license?package=<package_name>&sku=<product_id>&service=<account>&key=<key>&token=<token>
    

    , где:
    • package — название пакета вашего приложения, например: net.solargames.dungeonexplorer2
    • product_id — идентификатор вашей внутриигровой покупки, например: net.solargames.dungeonexplorer2.gold
    • service — e-mail вашего сервисного аккаунта из пункта 5 выше
    • key — название файла с ключом без расширения, в примере был «key»
    • token — токен покупки, выдается гуглом при совершении покупки на устройстве

    Ответ в случае успешной покупки:
    <?xml version="1.0" encoding="UTF-8"?><result consumptionState="1" purchaseState="0" purchaseTime="1405003881937" status="0" />
    

    и если кто-то подсунул неверный токен
    <?xml version="1.0" encoding="UTF-8"?><result message="Invalid Value" status="1" />
    

    отличаются статусом, в случае успеха он равен нулю.

    Тут разобраны внутренности проекта, для тех кому интересно
    Кишки

    Для работы требуется Google API Python Client, для удобства я статично добавил его в проект.
    Линк: code.google.com/p/google-api-python-client/source/browse

    Вся магия содержится в одной маленькой функции:
    from oauth2client.client import SignedJwtAssertionCredentials
    from apiclient.discovery import build
    
    credentials = SignedJwtAssertionCredentials(
          service,
          key_content,
          scope='https://www.googleapis.com/auth/androidpublisher')
    
    http = httplib2.Http()
    http = credentials.authorize(http)
    service = build("androidpublisher", "v1.1", http=http)
    result = service.inapppurchases().get(packageName=package, productId=sku, token=token).execute(http=http)
    


    Внутри result лежит dict с параметрами покупки. В случае ошибки возникнет Exception с довольно скудным описанием проблемы
    Метки:
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 12
    • –6
      Спасибо, мы обязательно учтем вашу защиту и выпустим патч.
      А вообще — не пробовали выпускать годные приложения, чтобы людям хотелось заплатить за покупку, а не выманивать у них деньги любыми путями?
      • +12
        Хакеров, читеров, ботоводов и прочую нечисть надо гнать из мультиплеера куда подальше. Даже если 99% игроков с радостью платит за покупку, 1% дебилов может испортить им все удовольствие.
        • 0
          От донатеров вреда для удовольствия не меньше, а даже больше, ибо с ними естественно нет борьбы со стороны разработчиков. Всегда находятся в играх те, у кого в реале с деньгами проблем вообще нет. И получается игра против чьего-то кошелька. Допуск в игру платный, а далее все должны быть в полностью одинаковых условиях. Бездонный кошелек реального мира не должен давать какие-либо бонусы в виртуальном.
          • 0
            Если делать игры с абонентской платой, в них будет играть меньше людей. Меньше людей — меньше удовольствия. Вот и приходится разработчикам балансировать между «уменьшениями удовольствия».
            От себя могу порекомендовать не играть в ММО, а играть в киберспортивные дисциплины — QuakeLive, Starcraft 2, Dota, LoL.
            • 0
              А я и не играю в них. Довольно давно бросил.
              • 0
                Я тоже. В SWTOR одну сюжетную линию прошел, может еще парочку пройду. Но в MMO-часть ввязываться не буду.
        • +6
          Не нравится приложение — не плати, в чем проблема? Странный какой-то подход: приложение мне нравится, но платить не нравится, поэтому я сломаю. Хм.
          • 0
            Добавь поддержку подписок лучше ;)
            • 0
              Я вообще не понимаю что в вашей голове творится.
              Плохо вас в в детстве воспитывали. Воровать и/или помогать воровать другим — плохо.
            • +4
              А зачем использовать гугловские апи?
              Достаточно просто проверить подпись чека на своем сервере.
              Например: robertomurray.co.uk/blog/2013/server-side-google-play-in-app-billing-receipt-validation-and-testing/
            • 0
              Кстати, если кто-то сомневается, стоит ли заморачиваться с проверкой ин-апов или и так сойдет, то такая небольшая статистика по одной моей игре — приблизительно 60% от общего количества внутреигровых покупок фейковые и идут через Freedom. Не верю, что хоть кто-нибудь из этих 60% заплатил бы, будь эта проверка встроена, но тем не менее бороться с этим лишним не будет.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.