¿Cómo ahorrar mejor el estado de compra de InApp localmente?

Estoy a punto de terminar mi primera aplicación, y una última cosa es implementar la facturación de IAP, por lo que estoy leyendo bastante acerca del tema (incluyendo problemas de seguridad como encriptación, ofuscación y otras cosas).

Mi aplicación es una versión gratuita, con la posibilidad de actualizar a la versión completa a través de IAP, por lo que sólo habría un elemento de compra administrado "premium". Tengo algunas preguntas sobre esto:

En el ejemplo de la API de Google IAP (trivialdrivesample), siempre hay un cheque IAP en MainActivity para ver si el usuario compró la versión premium, realizada a través de

MHelper.queryInventoryAsync (mGotInventoryListener);

Mi primera preocupación: Esto significa que el usuario siempre necesita tener una conexión de Internet / datos en la aplicación de inicio, para poder cambiar a la versión premium de la derecha? ¿Qué pasa si el usuario no tiene una conexión a Internet? Él iría con la versión lite supongo, que me encontraría molesto.

Así que pensé en cómo guardar el estado de isPremium localmente, ya sea en SharedPrefs o en la base de datos de aplicaciones. Ahora, sé que no se puede detener a un hacker para hacer ingeniería inversa de la aplicación, no importa lo que, incluso así porque no soy dueño de un servidor para hacer algún lado del servidor de validación.

Sin embargo, uno simplemente no puede guardar una bandera "isPremium" en algún lugar, ya que sería demasiado fácil de detectar.

Así que estaba pensando en algo como esto:

  • El usuario compra Premium
  • App obtiene el IMEI / Device-ID y XOR lo codifica con una clave de cadena codificada, lo guarda localmente en la base de datos de la aplicación.

Ahora, cuando el usuario vuelve a iniciar la aplicación:

  • La aplicación obtiene la cadena codificada de la base de datos, la decodifica y comprueba si decodedString == IMEI. Si sí -> prima
  • Si no, entonces la queryInventoryAsync normal será llamada para ver si el usuario compró premium.

¿Qué piensas de ese enfoque? Sé que no es super seguro, pero para mí es más importante que el usuario no está molesto (como con conexión a Internet obligatoria), que la aplicación será unhackable (que es imposible de todos modos). ¿Tienes otros consejos?

Otra cosa, que actualmente no tengo ni idea, es cómo restaurar el estado de la transacción cuando el usuario desinstala / reinstala la aplicación. Sé que la API tiene algún mecanismo para eso, y aditionally mi base de datos se puede exportar e importar a través de la aplicación (por lo que la bandera de isPremium codificado sería también exportable / importable). Ok, supongo que sería otra pregunta, cuando llegue el momento 😉

Cualquier idea y comentario a este enfoque son bienvenidos, ¿crees que es una buena solución? ¿O estoy perdiendo algo / dirigiéndome hacia alguna dirección equivocada?

Yo también estaba haciendo las mismas investigaciones, pero durante mis pruebas me di cuenta de que no es necesario almacenarlo, como Google hacer todo el almacenamiento en caché que necesita y sospecho (aunque no lo han investigado) que lo están haciendo de manera segura Como posible (viendo como en su interés también!)

Así que aquí es lo que hago

// 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 + ")."); } } }; 

Entonces, ¿qué sucede aquí?

El IAB está configurado y llama a startSetup , en una terminación exitosa (siempre y cuando se ha ejecutado una vez con una conexión a Internet y está configurado correctamente siempre tendrá éxito) llamamos queryInventoryAsync para averiguar lo que ya está comprado Ha sido llamado mientras está en línea siempre funciona sin conexión).

Así que si una compra se completa correctamente (como sólo se puede hacer mientras está en línea) que llamamos queryInventoryAsync para asegurarse de que se ha llamado mientras está en línea.

Ahora no hay necesidad de almacenar nada que ver con las compras y hace que su aplicación sea mucho menos hackable.

He probado de muchas maneras, modo de vuelo, apagar los dispositivos de nuevo y lo único que lo estropea es la eliminación de datos en algunas de las aplicaciones de Google en el teléfono (No es probable que suceda!).

Por favor contribuya a esto si tiene diferentes experiencias, mi aplicación todavía está en la fase de pruebas tempranas.

Refactore la respuesta de ne0 en un método estático, incluyendo los comentarios de snark.

Llamo a este método cuando empiece mi aplicación – necesitarás habilitar tus funciones en 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."); } } }); } } }); } 

Esto funcionará a través de reinicios y sin una conexión de red, siempre que el usuario haya comprado el elemento.

Ya que ya sabes que es imposible hacer que sea unhackable con este sistema, yo recomendaría no tratar de evitar la piratería. Lo que usted está proponiendo se conoce como "Seguridad a través de la oscuridad" y suele ser una mala idea.

Mi consejo sería intentar queryInventoryAsync() primero, y solo revisar su bandera 'isPremium' si no hay conexión a Internet.

También hay algunas otras posibles formas de hacer esto, como tener aplicaciones independientes gratuitas y premium, en lugar de una compra en la aplicación. Cómo otras personas manejan esto y las herramientas que Google pone a disposición podría justificar una investigación.

queryInventoryAsync tomará automáticamente en cuenta desinstalar y reinstala, ya que realiza un seguimiento de las compras para el usuario conectado.

  • ¿Cómo puedo asegurar que el archivo se transmita desde el teléfono al servidor sin ser manipulado?
  • ¿Qué herramientas existen que ofrecen protección apk para android (contra la inversión y el reenvasado)?
  • Seguridad de la carpeta de activos de Android
  • ¿Qué tan seguros son los archivos SQLite y SharedPreferences en Android?
  • ¿Dónde almacenar datos de aplicaciones grandes en dispositivos android?
  • ¿Cómo almacenar de forma segura el token de acceso y el secreto en Android?
  • Cómo habilitar la opción AutoStart para mi aplicación en Xiaomi phone Security App programmatically en android
  • ¿Cómo puedo evitar que alguien se comunique con mi servidor, excepto mi aplicación de Android?
  • Evitar que el usuario tome fotos de la pantalla de la aplicación
  • Cómo agregar PBKDF2WithHmacSHA1 para android api 8 (Froyo)
  • Android java.security.cert.CertPathValidatorException: Ancla de confianza para la ruta de certificación no encontrada
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.