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:
- Crea una instancia de
BillingClient
. - Implementa un
BillingClientStateListener
para recibir devoluciones de llamadas sobre el estado del servicio. - Llama a
startConnection()
en tu instancia deBillingClient
. - Quita el código de
onActivityResult()
relacionado con la compra directa desde la aplicación y muévelo aPurchasesUpdatedListener
.
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:
- Convierte el objeto
Bundle
de los detalles del SKU enSkuDetailsParams
. - Cambia la llamada
mService.getSkuDetails()
para que useBillingClient.querySkuDetailsAsync()
. - Convierte el objeto
Bundle
de tu intent de compra en un objetoBillingFlowParams
. - Cambia la llamada
mService.getBuyIntent()
para que useBillingClient.launchBillingFlow()
. - Quita cualquier fragmento de código relacionado con compras directas desde la aplicación de
onActivityResult()
y muévelo aPurchasesUpdatedListener
.
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 lugar de llamar a
consumePurchase()
, llama aBillingClient.consumeAsync()
. - Implementa
ConsumeResponseListener
.
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 deonResume()
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 tuPurchasesUpdatedListener
.
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()
.