IAP en Aplicaciones Android

Algunos dicen que la forma más fácil de hacer conocida tu aplicación es disminuir las barreras de entrada al máximo, i.e., que sea gratis. Ya lo decía Chris Anderson a mediados del 2009 en Free: The Future of a Radical Price:

“Give a product away, and it can go viral. Charge a single cent for it and you’re in an entirely different business. . . . The truth is that zero is one market and any other price is another.”

Puedo dar fe que es verdad. Sube una aplicación gratuita y no le digas a nadie, no hagas nada, vuelve en una semana y algún número de descargas tendrá.

El punto es:  ¿Cómo monetizar las aplicaciones gratuitas?
A nadie le gusta la publicidad (excepto a los publicistas, creo), así que una excelente opción son las controversiales in-app purchases. Son un genial recurso para aumentar las capacidades de la aplicación y darle dinamismo. Claro que no hay que abusar, un mal uso de ellas le causa un daño a tu aplicación, a tu marca, a los usuarios, y a la industria. Un interesante análisis sobre como hacer buen uso de in-app purchase lo puedes leer acá.
También recomiendo el libro Free-to-Play: Making Money From Games You Give Awaypara los desarrolladores de videojuegos allá afuera. Habla en extenso sobre el exitoso modelo F2P. Éxito obtenido en gran parte gracias a las in-app purchases.

Compras In-app

Google Play nos permite realizar dos tipos de IAP (in-app purchases): productos y suscripciones. En este artículo nos enfocaremos en la primera.
Hay dos áreas involucradas en la implementación:

  • Consola de desarrollador en Google Play.
  • Nuestra aplicación.

Para este artículo voy a utilizar de ejemplo mi aplicación Admob Client. Esta aplicación tiene incluidas donaciones en forma de in-app purchase.

Preparación

1. Primero hay que agregar los productos al google developer console

Debemos ir a la consola de desarrollador y luego a In-app products. Aquí se presenta el panel de administración de tus productos, como podemos ver acá:

productos in-app en google play

Aquí se agregan los productos que se planean vender.

2. Agregar la biblioteca IInAppBillingService.aidl

Para que tu aplicación se comunique con el servicio de In-app Billing de Google Play Services tiene que usar la interfaz IInAppBillingService.aidl (Android Interface Definition Language – AIDL). Para obtenerlo hay que:

  • Abrir el Android SDK Manager
  • Ir a la sección extras
  • Seleccionar Google Play Billing Library
  • Instalar los paquetes

Luego el archivo se encontrará en:

<sdk>/extras/google/play_billing/IInAppBillingService.aidl

Hay que copiarlo a tu proyecto, al path:

/src/com/android/vending/billing/IInAppBillingService.aidl

3. Setear el permiso

En tu Android Manifest agregar el permiso de billing:

<uses-permission android:name="com.android.vending.BILLING" />

Implementación

Para facilitar la implementación del servicio es recomendable usar las clases de ayuda que vienen en el ejemplo al descargar la biblioteca en el paso 2. El código de ejemplo está ubicado en:

<sdk>/extras/google/play_billing/samples/TrivialDrive/

1. Inicializar Conexión con Google Billing

IabHelper iab_helper; //la clase IabHelper se incluyó desde el ejemplo TrivialDrive

private void setupIAP() {
		
    iab_helper = new IabHelper(this, getBase64EncodedPublicKey());
    iab_helper.enableDebugLogging(true, LoginActivity.TAG);
    iab_helper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
              public void onIabSetupFinished(IabResult result) {
	          if (!result.isSuccess()){
		       // Ocurrió un problema
		       Log.d(TAG,"Problema al iniciar IAP billing: " + result);
		   }
	       }
	 });
}

Al constructor de IabHelper hay que pasarle el contexto y la llave pública del servicio de billing. Esta la obtenemos en la consola de desarrollador de Google Play, en la sección Services & APIs, bajo el título LICENSING & IN-APP BILLING.
Es importante desvincular del servicio cuando se destruya la actividad:

@Override
protected void onDestroy() {
    super.onDestroy();
    if (iab_helper != null)
        iab_helper.dispose();
    iab_helper = null;
}

2. Consultar por productos disponibles

Luego de establecer la conexión hay que consultar por los productos disponibles. Para esto usamos el método queryInventoryAsync(boolean , List , QueryInventoryFinishedListener). El primer parámetro es si trae los detalles o no, el segundo parámetro es la lista de productos a traer, y el tercer parámetro es el listener para manejar la respuesta.

List<String> additionalSkuList = new ArrayList<String>();
additionalSkuList.add("coffee_for_developers");
additionalSkuList.add("lunch_for_developers");
additionalSkuList.add("romantic_dinner_for_developers");

iab_helper.queryInventoryAsync(true, additionalSkuList,mQueryFinishedListener);

Como podemos ver, acá le indico que traiga la información de los productos con ID/SKU: “coffee_for_developers”, “lunch_for_developers” y “romantic_dinner_for_developers”. Y le indico el siguiente listener:

IabHelper.QueryInventoryFinishedListener mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {

    public void onQueryInventoryFinished(IabResult result, Inventory inventory) {		
        if (result.isFailure()) {
            Log.d(TAG, "Falló algo:" + result.getMessage());
            return;
        }

        donations.add(inventory.getSkuDetails("coffee_for_developers"));
	donations.add(inventory.getSkuDetails("lunch_for_developers"));
	donations.add(inventory.getSkuDetails("romantic_dinner_for_developers"));
	
        //Actualizo el UI para que se muestren los productos.
        ((DonationsAdapter) list_donations.getAdapter()).notifyDataSetChanged();
	}
};

ArrayList<SkuDetails> donations = new ArrayList<SkuDetails>(3);

Si el resultado es exitoso, el detalle de los productos es almacenado en una clase Inventory (también provista por el código de ejemplo). Y se puede actualizar el UI -en este caso es un ListView- con los datos necesarios.
En mi aplicación los productos se muestran así:

Lista productos
Lista productos

Ahí vemos el ListView que contiene los tres productos.

3. Comprar productos

Para realizar una compra hay que llamar al método launchPurchaseFlow(Activity, String, int, OnIabPurchaseFinishedListener, String). Los parámetros son:

  • Activity  es para darle el contexto
  • El ID/SKU del producto, tiene que ser el mismo registrado en la consola de desarrollador
  • Código a capturar en onActivityResult cuando se vuelva a la actividad.
  • Listener para recibir la respuesta desde Google Play
  • El llamado “developer payload”. Es un String usado para enviar información adicional de la compra, para identificar el usuario por ejemplo. Especialmente útil para los bienes no-consumibles, cuando se consulte por el producto, este String vendrá en la respuesta, así se puede confirmar que es una compra legítima y corresponde al usuario. En este caso envío un String aleatorio ya que son bienes consumibles.
protected static final int PURCHASE_REQUEST_CODE = 1234;

public void comprar(String sku_producto){

    iap_helper.launchPurchaseFlow(this, 
                      sku_producto, 
                      PURCHASE_REQUEST_CODE, 
                      mPurchaseFinishedListener, 
                      Utils.randomString(40));
}

El listener que maneja la respuesta es este:

IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
    public void onIabPurchaseFinished(IabResult result, Purchase purchase) {

	  if (result.isFailure()) {
		Log.d(TAG, "Error en la compra: " + result);
		return;
	  } else {
		Log.d(LoginActivity.TAG, "compra exitosa:"+purchase.getSku());

                if (purchase.getSku().equals(SKU_NO_CONSUMIBLE)) {
                    // Hace los cambios respectivos en la aplicación
                }
                if(purchase.getSKU().equals(SKU_CONSUMIBLE){
                    iap_helper.consumeAsync(purchase, mConsumeFinishedListener);
                }
   }
};

Si los productos fueran no-consumibles, por ejemplo pagar por la versión premium, o pagar para quitar los Ads de la aplicación, se realizan los cambios respectivos y se guarda el nuevo estado “de pago” del usuario. Si los bienes son consumibles, por ejemplo una bomba en un juego, monedas virtuales, o una donación en este caso, se debe llamar al método consumeAsync, que consume el producto.

4. Consumir productos

public void consumirProducto(Purchase purchase){

    iap_helper.consumeAsync(purchase, mConsumeFinishedListener);

}

El listener que recibe la respuesta:

IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {

    public void onConsumeFinished(Purchase purchase, IabResult result) {

        //Si la actividad ya ha sido destruida cuando termina el proceso.
        if (iap_helper == null)
	    return;

        if (result.isSuccess()) {
	    Log.d(TAG,"producto consumido: " + purchase.getSku()+ ", result: " + result);
            if (purchase.getSku().equals("coffee_for_developers")) {
                //Aquí se realiza el comportamiento dependiendo del producto
                Log.d(TAG, "Consumption successful COFFEE");
            }

            saveData();
            getActivity().findViewById(R.id.layoutDevelopersLove).setVisibility(View.VISIBLE);
         } else {
            Log.d(TAG, "Error al consumir producto: " + result);
	 }
     }
};

5. Consultar por productos comprados

Es buena práctica consultar al inicio de la aplicación -o cuando sea necesario- que productos tiene comprados el usuario. Para esto se usa el método queryInventoryAsync(QueryInventoryFinishedListener), este método ya lo usamos antes, pero esta vez sólo pasamos el listener como parámetro, como se pueden imaginar es un método sobrecargado.

public void consultarProductos(){

  iab_helper.queryInventoryAsync(mGotInventoryListener);

}

Y el listener:

IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
    public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
 
    if (result.isFailure()) {
        // Error
      }
      else {
        // Buscar los productos que tengan relevancia
        tieneProducto = inventory.hasPurchase(SKU_PRODUCTO_IMPORTANTE);
        if(tieneProducto)
          //Modificar comportamiento app o actualizar UI 
      }
   }
};

9. Testing app

Google Play no permite realizar compras con la misma cuenta del desarrollador que sube las aplicaciones, por lo que para probar las in-app purchase hay que crear cuentas de prueba. Esto se hace en la consola de desarrollo en Google Play. Hay que ir a Settings->Account details, y bajar hasta la sección License Testing:

cuenta de prueba de billng

Luego hay que exportar la aplicación con el certificado de producción(no debug), subirlo a Google Play, y ser descargado en el equipo asociado a la cuenta de prueba. Si, yo también creo que es un proceso excesivamente engorroso.

Finalmente cuando todo esté en orden puedes publicar tu aplicación, sentarte y esperar el flujo de cash.

Leave a Reply

Your email address will not be published. Required fields are marked *