Guía de migración del AIDL a la Biblioteca de Facturación Google Play

En este tema, se describe cómo migrar desde una integración de la facturación que usa el Lenguaje de definición de la interfaz de Android (AIDL). Dejó de estar disponible la posibilidad de acceder al sistema de facturación de Google Play con el AIDL, y todas las integraciones deberán usar la Biblioteca de la Facturación Google Play en el futuro.

Pasos de la migración

Importa la Biblioteca de Facturación Google Play

Primero, agrega una dependencia a la Biblioteca de Facturación Google Play. Si usas Gradle, puedes agregar lo siguiente al archivo build.gradle de tu app:

Groovy

dependencies {
    def billing_version = "6.0.1"

    implementation "com.android.billingclient:billing:$billing_version"
}

Kotlin

dependencies {
    val billing_version = "6.0.1"

    implementation("com.android.billingclient:billing:$billing_version")
}

Puedes borrar cualquier código de "unión", como IabHelper, que tal vez copiaste del código de referencia anterior. La función que ofrece IabHelper ahora es parte de la Biblioteca de Facturación Google Play.

Quita el permiso com.android.vending.BILLING

La Biblioteca de Facturación Google Play incorpora el permiso de com.android.vending.BILLING dentro de su manifiesto. Ya no es necesario agregar ese permiso explícitamente dentro del manifiesto de tu app.

Conéctate a la Facturación Google Play

El método BillingClient de la Biblioteca de Facturación Google Play se encarga de administrar la conexión. Para llevar a cabo la migración, realiza los siguientes cambios en tu app:

En los siguientes ejemplos, se muestra cómo se vería tu app antes y después de realizar estos cambios:

Antes

mServiceConn = new ServiceConnection() {
    @Override
    public void onServiceDisconnected(ComponentName name) {
      ...
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      ...
    }
};

Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
List<ResolveInfo> intentServices = mContext.getPackageManager()
    .queryIntentServices(serviceIntent, 0);
if (intentServices != null && !intentServices.isEmpty()) {
    mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
} else {
    // Handle errors.
    ...
}

...

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    IabResult result;
    if (requestCode != mRequestCode || data == null) {
        // Handle errors.
        ...
    }

    int responseCode = getResponseCodeFromIntent(data);
    String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA);
    String dataSignature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE);

    if (resultCode != Activity.RESULT_OK || responseCode != BILLING_RESPONSE_RESULT_OK) {
        // Handle errors.
        ...
    }

    // Process successful purchase.
    ...

    return true;
}

Después

Kotlin

class MyBillingImpl(private var billingClient: BillingClient) : PurchasesUpdatedListener {

    init {
        billingClient = BillingClient.newBuilder(activity).setListener(this).build()
        billingClient.startConnection(object : BillingClientStateListener {
            override fun onBillingSetupFinished(billingResult: BillingResult?) {
                // Logic from ServiceConnection.onServiceConnected should be moved here.
            }

            override fun onBillingServiceDisconnected() {
                // Logic from ServiceConnection.onServiceDisconnected should be moved here.
            }
        })
    }

    override fun onPurchasesUpdated(
        billingResult: BillingResult?,
        purchases: MutableList<Purchase>?
    ) {
        // Logic from onActivityResult should be moved here.
    }
}

Java

public class MyBillingImpl implements PurchasesUpdatedListener {
    private BillingClient billingClient;
    ...

    public void initialize() {
        billingClient = BillingClient.newBuilder(activity).setListener(this).build();
        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                // Logic from ServiceConnection.onServiceConnected should be moved here.
            }

            @Override
            public void onBillingServiceDisconnected() {
                // Logic from ServiceConnection.onServiceDisconnected should be moved here.
            }
        });
    }

    @Override
    public void onPurchasesUpdated(
        @BillingResponse int responseCode, @Nullable List<Purchase> purchases) {
        // Logic from onActivityResult should be moved here.
    }
}

Cómo realizar una compra

Para iniciar el cuadro de diálogo de compra, haz lo siguiente:

En los siguientes ejemplos, se muestra cómo se vería tu app antes y después de realizar estos cambios:

Antes

// Query Skus
String skuToSell = "premium_upgrade";
ArrayList<String> skus = new Arraylist<>();
skus.add(skuToSell);
Bundle querySkus = new Bundle();
querySkus.putStringArrayList(GET_SKU_DETAILS_ITEM_LIST, skus);
Bundle skuDetails = mService.getSkuDetails(3,
                                           mContext.getPackageName(),
                                           itemType,
                                           querySkus);

if (!skuDetails.containsKey(RESPONSE_GET_SKU_DETAILS_LIST)) {
    // Handle errors.
    ...
}

// Launch Buy Flow
Bundle buyIntentBundle = mService.getBuyIntent(3,
                                               mContext.getPackageName(),
                                               skuToSell,
                                               "Inapp",
                                               "");

int response = getResponseCodeFromBundle(buyIntentBundle);
if (response != BILLING_RESPONSE_RESULT_OK) {
    // Handle errors.
    ...
}

PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT);
act.startIntentSenderForResult(pendingIntent.getIntentSender(),
                               requestCode,
                               new Intent(),
                               Integer.valueOf(0),
                               Integer.valueOf(0),
                               Integer.valueOf(0));

// Purchase is handled in onActivityResult illustrated in the previous section.

Después

Kotlin

val skuToSell = "premium_upgrade"
val skuList = mutableListOf<String>()
skuList.add(skuToSell)
val params = SkuDetailsParams.newBuilder()
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP)
billingClient.querySkuDetailsAsync(params.build(),
  object : SkuDetailsResponseListener {
    override fun onSkuDetailsResponse(
      billingResult: BillingResult?, skuDetailsList: MutableList<SkuDetails>?) {
        // Process the result.
        }
    })

// SkuDetails object obtained above.
val skuDetails = ...
val purchaseParams = BillingFlowParams.newBuilder()
  .setSkuDetails(skuDetails)
   .build()

billingClient.launchBillingFlow(activity, purchaseParams)

// Purchase is handled in onPurchasesUpdated illustrated in the previous section

Java

String skuToSell = "premium_upgrade";
List<String> skuList = new ArrayList<> ();
skuList.add(skuToSell);
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(SkuType.INAPP);
billingClient.querySkuDetailsAsync(params.build(),
    new SkuDetailsResponseListener() {
        @Override
        public void onSkuDetailsResponse(BillingResult billingResult,
                                         List<SkuDetails> skuDetailsList) {
            // Process the result.
            ...
        }
    });

// SkuDetails object obtained above.
SkuDetails skuDetails = ...;

BillingFlowParams purchaseParams =
    BillingFlowParams.newBuilder()
            .setSkuDetails(skuDetails)
            .build();

mBillingClient.launchBillingFlow(mActivity, purchaseParams);

// Purchase is handled in onPurchasesUpdated illustrated in the previous section.

Cómo consumir compras

Para consumir compras con la Biblioteca de Facturación Google Play, haz lo siguiente:

En los siguientes ejemplos, se muestra cómo se vería tu app antes y después de realizar estos cambios:

Antes

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    int responseCode = data.getIntExtra(RESPONSE_CODE);
    JSONObject purchaseData =
        new JSONObject(data.getStringExtra("INAPP_PURCHASE_DATA"));

    String token = purchaseData.get("purchaseToken");

    ...

    // Consume purchase
    int response = mService.consumePurchase(3, mContext.getPackageName(), token);
    if (response != BILLING_RESPONSE_RESULT_OK) {
        // Handle errors.
        ...
    }

    // Handle successful consumption.
}

Después

Kotlin

class MyBillingImpl(private val billingClient: BillingClient) :
        ... , ConsumeResponseListener {

  fun consumePurchase(purchaseToken: String) {
    val consumeParams = ConsumeParams
      .newBuilder()
      .setPurchaseToken(purchaseToken)
      .build()
  }

  override fun onConsumeResponse(
    billingResult: BillingResult?,
    purchaseToken: String?) {
      // Handle consumption
    }
 }

Java

public class MyBillingImpl implements ..., ConsumeResponseListener {
    private BillingClient billingClient;
    ...

    public void consumePurchase(String purchaseToken) {
        ConsumeParams consumeParams =
        ConsumeParams.newBuilder()
                .setPurchaseToken(purchaseToken)
                .build();
    }

    @Override
    void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
        // Handle consumption.
        ...
    }
}

Cómo procesar compras

A partir de la versión 2.0 de la Biblioteca de Facturación Google Play, tu app debe consumir o procesar todas las compras.

Si no consumes ni procesas una compra en un plazo de tres días, Google revocará automáticamente la compra y le reembolsará el monto al usuario. Para obtener más información, consulta Cómo procesar una compra.

Cómo reconocer las compras fuera de la app

Para migrar el control de compras fuera de la app a la Biblioteca de Facturación Google Play, haz lo siguiente:

  • Asegúrate de que tu app llame a BillingClient.queryPurchasesAsync() en la devolución de llamada de onResume() de tu app.
  • Quita el receptor de emisión de com.android.vending.billing.PURCHASES_UPDATED y mueve el código de devolución de llamada correspondiente a tu PurchasesUpdatedListener.

Antes, cuando se integraba la Facturación Google Play con el AIDL, tu app debía registrar un objeto de escucha para recibir el intent de com.android.vending.billing.PURCHASES_UPDATED a fin de procesar las compras realizadas fuera de tu app.

Con la Biblioteca de Facturación Google Play, tu app siempre debe llamar a queryPurchasesAsync() en la devolución de llamada de onResume() de tu app como primer paso para garantizar que se reconozcan todas las compras que se hicieron mientras no se estaba ejecutando la app. Mientras se ejecuta tu app, la Biblioteca de Facturación Google Play detecta automáticamente las compras que se realizan fuera de ella y te envía notificaciones a través de PurchasesUpdatedListener.

Cómo administrar transacciones pendientes

A partir de la versión 2.0 de la Biblioteca de Facturación Google Play, tu app debe administrar transacciones pendientes que requieran una acción adicional después de realizar la compra antes de otorgarle el derecho correspondiente. Por ejemplo, un usuario puede elegir que desea comprar tu producto integrado en la aplicación en una tienda física con dinero en efectivo. Esto significa que la transacción se completa fuera de la app. En este caso, deberías otorgar derechos solo después de que el usuario haya completado la transacción.

Para obtener más información, consulta Cómo admitir transacciones pendientes.

Carga útil para desarrolladores

La carga útil para desarrolladores se utilizó históricamente para diversos fines, por ejemplo, la prevención de fraudes y la atribución de compras al usuario correcto. Ahora que la Biblioteca de Facturación Google Play admite estos casos de uso, hemos dejado de usar la carga útil para desarrolladores a partir de la versión 2.2 de la Biblioteca de Facturación Google Play. Para obtener más información, consulta Carga útil para desarrolladores.

Mensajes de error detallados

A partir de la versión 2.0 de la Biblioteca de Facturación Google Play, todos los errores tienen mensajes relacionados con la depuración. Para obtener esos mensajes, llama a BillingResult.getDebugMessage().