Подтвердить что ты не робот

Как лучше сохранить статус покупки InApp локально?

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

Мое приложение - это бесплатная версия с возможностью обновления до полной версии через IAP, так что будет только один элемент покупной покупки "премиум". У меня есть несколько вопросов по этому поводу:

В примере API API IAP (trivialdrivesample) всегда есть проверка IAP в MainActivity, чтобы узнать, покупает ли пользователь премиум-версию, выполненную с помощью

mHelper.queryInventoryAsync(mGotInventoryListener);

Моя первая забота: Это означает, что пользователь всегда должен иметь подключение к Интернету/данным при запуске приложения, чтобы иметь возможность перейти на премиум-версию вправо? Что делать, если у пользователя нет подключения к Интернету? Думаю, он подойдет с облегченной версией, что мне будет неприятно.

Итак, я подумал о том, как сохранить статус isPremium локально, либо в SharedPrefs, либо в базе данных приложений. Теперь я знаю, что вы не можете остановить хакера, чтобы перепроектировать приложение, несмотря ни на что, даже если я не владею сервером для проверки на стороне сервера.

Тем не менее, просто невозможно сохранить флаг "isPremium" где-то, так как это было бы слишком легко заметить.

Итак, я думал о чем-то вроде этого:

  • Пользователь покупает премиум
  • Приложение получает идентификатор IMEI/Device-ID, а XOR кодирует его с помощью жестко закодированного ключа String, сохраняет его локально в базе данных приложений.

Теперь, когда пользователь снова запустит приложение:

  • Приложение получает закодированную строку из базы данных, декодирует ее и проверяет, расшифрован ли файлString == IMEI. Если да → премиум
  • Если нет, тогда будет вызван обычный запросInventoryAsync, чтобы узнать, покупает ли пользователь премию.

Что вы думаете об этом подходе? Я знаю, что это не supersecure, но для меня важнее, чтобы пользователь не был раздражен (например, с обязательным подключением к Интернету), чем приложение будет недоступно (что в любом случае невозможно). У вас есть другие советы?

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

Любые мысли и комментарии к этому подходу приветствуются, считаете ли вы, что это хорошее решение? Или я пропущу что-то/заголовок в неправильном направлении?

4b9b3361

Ответ 1

Я тоже делал те же исследования, но во время тестирования я понял, что вам не нужно его хранить, так как Google делает все необходимое для кеширования, и я подозреваю (хотя я еще не исследовал его), что они делают так как надежно (насколько это возможно в их интересах!)

Итак, вот что я делаю

// Done in onCreate
mHelper = new IabHelper(this, getPublicKey());

mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
      if (!result.isSuccess()) {
         // Oh noes, there was a problem.
         Log("Problem setting up In-app Billing: " + result);
      } else {
         Log("onIabSetupFinished " + result.getResponse());
         mHelper.queryInventoryAsync(mGotInventoryListener);
     }
    }
});

// Called by button press
private void buyProUpgrade() {
    mHelper.launchPurchaseFlow(this, "android.test.purchased", 10001,   
           mPurchaseFinishedListener, ((TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId());
}

// Get purchase response
private IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
    public void onIabPurchaseFinished(IabResult result, Purchase purchase) 
    {
       if (result.isFailure()) {
          Log("Error purchasing: " + result);
          return;
       }      
       else if (purchase.getSku().equals("android.test.purchased")) {
      Log("onIabPurchaseFinished GOT A RESPONSE.");
              mHelper.queryInventoryAsync(mGotInventoryListener);
      }
    }
};

// Get already purchased response
private IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
    public void onQueryInventoryFinished(IabResult result,
       Inventory inventory) {

       if (result.isFailure()) {
         // handle error here
           Log("Error checking inventory: " + result); 
       }
       else {
         // does the user have the premium upgrade?        
         mIsPremium = inventory.hasPurchase("android.test.purchased");        
         setTheme();

         Log("onQueryInventoryFinished GOT A RESPONSE (" + mIsPremium + ").");
       }
    }
};

Итак, что здесь происходит?

IAB настроен и вызывает startSetup, при успешном завершении (если он был запущен один раз с подключением к Интернету и настроен правильно, он всегда будет успешным), мы вызываем queryInventoryAsync, чтобы узнать, что (опять же, если это было вызвано во время онлайн, он всегда работает в автономном режиме).

Итак, если покупка завершена успешно (как это может быть сделано только в режиме онлайн), мы вызываем queryInventoryAsync, чтобы убедиться, что она была вызвана в режиме онлайн.

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

Я тестировал это много способов, режим полета, снова отключая устройства, и единственное, что его испортило, - это очистка данных в некоторых приложениях Google на телефоне (вряд ли произойдет!).

Пожалуйста, внесите свой вклад в это, если у вас есть другой опыт, мое приложение все еще находится на ранней стадии тестирования.

Ответ 2

Я рефакторизую ne0 ответ на статический метод, включая комментарии от snark.

Я вызываю этот метод при запуске приложения - вам нужно включить свои функции в TODO

/**
 * This is how you check with Google if the user previously purchased a non-consumable IAP
 * @param context App Context
 */
public static void queryPlayStoreForPurchases(Context context)
{
    final IabHelper helper = new IabHelper(context, getPublicKey());

    helper.startSetup(new IabHelper.OnIabSetupFinishedListener() 
    {
         public void onIabSetupFinished(IabResult result) 
         {
               if (!result.isSuccess()) 
               {
                   Log.d("InApp", "In-app Billing setup failed: " + result);
               } 
               else 
               {  
                    helper.queryInventoryAsync(false, new IabHelper.QueryInventoryFinishedListener()
                    {
                        public void onQueryInventoryFinished(IabResult result, Inventory inventory)
                        {
                            // If the user has IAP'd the Pro version, let 'em have it.
                            if (inventory.hasPurchase(PRO_VERSION_SKU))
                            {
                                //TODO: ENABLE YOUR PRO FEATURES!! 

                                Log.d("IAP Check", "IAP Feature enabled!");
                            }
                            else
                            {
                                Log.d("IAP Check", "User has not purchased Pro version, not enabling features.");

                            }
                        }
                    });
               }
         }
    });
}

Это будет работать во всех перезагрузках и без сетевого подключения, если пользователь приобретет элемент.

Ответ 3

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

Мой совет: сначала попробовать queryInventoryAsync() и только проверить свой флаг isPremium, если нет подключения к Интернету.

Есть также несколько потенциальных других способов обойти это, например, иметь отдельные бесплатные и премиальные приложения вместо покупки приложения. Как другие люди справляются с этим, и инструменты, которые Google предоставляет, могут потребовать расследования.

queryInventoryAsync будет автоматически принимать во внимание удаление и переустановку, поскольку он отслеживает покупки для зарегистрированного пользователя.