
Trends-insights
septiembre 6, 2022
Updated: marzo 20, 2023
Las compras dentro de la aplicación (In-app purchase (IAP)), especialmente las suscripciones (subscriptions), son los métodos más populares para monetizar una aplicación. Por un lado, la suscripción permite al desarrollador invertir en el desarrollo del contenido y del producto, y por otro lado, ayuda a los usuarios a obtener una aplicación (app) de mayor calidad en general. Las compras dentro de la aplicación están sujetas a una comisión del 30%, pero si un usuario ha estado suscrito durante más de un año o una aplicación gana menos de 1$ al año, la comisión es del 15%.
En este artículo, te explicaremos cómo:
Antes de empezar, asegúrate de:
Ahora, pongamos manos a la obra y creemos nuestro primer producto.
Cambia a tu cuenta de desarrollador y elige la aplicación.
A continuación, en el menú de la izquierda, busca la sección Productos, selecciona Subscriptions y haz clic en Create a Subscription.
A continuación, veremos el configurador de la suscripción. Estos son algunos puntos importantes.
Desplázate hacia abajo y elige el periodo de suscripción. En nuestro caso, es una semana. Establece el precio.
Normalmente, estableces el precio en la moneda básica de la cuenta, y el sistema convierte el precio automáticamente. Pero también puedes editar manualmente el precio para un país determinado.
Por favor, ten en cuenta que Google muestra el impuesto para cada país. Es genial, en cambio App Store Connect no lo hace.
Desplázate hacia abajo y elige (si es necesario):
Independientemente de que las suscripciones se monetizan más eficazmente en iOS, el tablero de administración de Play Console es más cómodo, está mejor organizado y localizado, y funciona más rápido.
El proceso de creación de productos es lo más sencillo posible. Aquí te explicamos cómo crear productos en iOS.
Una vez creados los productos, vamos a trabajar en la arquitectura para aceptar y procesar las compras. En general, el proceso tiene el siguiente aspecto:
En esta parte, vamos a examinar más detalladamente los dos primeros puntos.
Añadir la Biblioteca de Facturación a un proyecto:
implementation "com.android.billingclient:billing:4.0.0"
En el momento de escribir esto, la última versión es la 4.0.0. Podrás sustituirla por cualquier otra en cualquier momento.
Vamos a crear una clase contenedora que cubra la lógica de interacción con Google Play e inicialice BillingClient desde la Biblioteca de Facturación en la misma. Llamemos a esta clase BillingClientWrapper.
Esta clase implementará la interfaz PurchasesUpdatedListener. Ahora anularemos un método para ello: onPurchasesUpdated(billingResult: BillingResult, purchaseList: MutableList<Purchase>?): es necesario justo después de que se realice una compra, pero describiremos el proceso de implementación en el próximo artículo.
import android.content.Context
import com.android.billingclient.api.*
class BillingClientWrapper(context: Context) : PurchasesUpdatedListener {
private val billingClient = BillingClient
.newBuilder(context)
.enablePendingPurchases()
.setListener(this)
.build()
override fun onPurchasesUpdated(billingResult: BillingResult, purchaseList: MutableList<Purchase>?) {
// here come callbacks about new purchases
}
}
Google recomienda evitar tener más de una conexión activa entre BillingClient y Google Play a fin de evitar que una devolución de llamada sobre una compra realizada se ejecute varias veces. Así, deberías tener un único BillingClient en una clase singleton. La clase del ejemplo no es un singleton, pero podemos utilizar la inyección de dependencia (por ejemplo, con la ayuda de Dagger o Koin) de esta forma, permitiendo que sólo exista una instancia en un momento dado.
Para realizar cualquier solicitud con la Biblioteca de Facturación, BillingClient debe tener una conexión activa con Google Play en el momento en que se realiza la solicitud, pero la conexión puede perderse en algún momento. Por comodidad, vamos a escribir una envoltura que nos permita realizar cualquier solicitud sólo cuando la conexión esté activa.
Para obtener los productos, necesitamos sus ID que establecimos en el mercado. Pero no basta con una solicitud, también necesitamos el tipo de producto (suscripciones o compras únicas), por eso podemos obtener una lista general de productos «combinando» los resultados de dos solicitudes.
La solicitud de productos es asíncrona, por lo que necesitamos una devolución de llamada que nos proporcione una lista de productos o devuelva un modelo de error. Al ocurrir un error, la Biblioteca de Facturación devuelve uno de sus BillingResponseCodes, así como el debugMessage. Vamos a crear una interfaz de devolución de llamada y un modelo para un error:
interface OnQueryProductsListener {
fun onSuccess(products: List < SkuDetails > )
fun onFailure(error: Error)
}
class Error(val responseCode: Int, val debugMessage: String)
Aquí está el código de un método privado para obtener datos sobre un tipo específico de productos y un método público que «combinará» los resultados de dos solicitudes y proporcionará al usuario la lista final de productos o mostrará un mensaje de error.
fun queryProducts(listener: OnQueryProductsListener) {
val skusList = listOf("premium_sub_month", "premium_sub_year", "some_inapp")
queryProductsForType(
skusList,
BillingClient.SkuType.SUBS
) { billingResult, skuDetailsList ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
val products = skuDetailsList ?: mutableListOf()
queryProductsForType(
skusList,
BillingClient.SkuType.INAPP
) { billingResult, skuDetailsList ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
products.addAll(skuDetailsList ?: listOf())
listener.onSuccess(products)
} else {
listener.onFailure(
Error(billingResult.responseCode, billingResult.debugMessage)
)
}
}
} else {
listener.onFailure(
Error(billingResult.responseCode, billingResult.debugMessage)
)
}
}
}
private fun queryProductsForType(
skusList: List<String>,
@BillingClient.SkuType type: String,
listener: SkuDetailsResponseListener
) {
onConnected {
billingClient.querySkuDetailsAsync(
SkuDetailsParams.newBuilder().setSkusList(skusList).setType(type).build(),
listener
)
}
}
Así, obtendremos información valiosa sobre los productos (SkuDetails), donde podremos ver los nombres localizados, los precios, el tipo de producto, así como el periodo de facturación y la información sobre el precio de lanzamiento y el periodo de prueba (si está disponible para este usuario) para las suscripciones. Este es el aspecto de la clase final:
import android.content.Context
import com.android.billingclient.api.*
class BillingClientWrapper(context: Context) : PurchasesUpdatedListener {
interface OnQueryProductsListener {
fun onSuccess(products: List<SkuDetails>)
fun onFailure(error: Error)
}
class Error(val responseCode: Int, val debugMessage: String)
private val billingClient = BillingClient
.newBuilder(context)
.enablePendingPurchases()
.setListener(this)
.build()
fun queryProducts(listener: OnQueryProductsListener) {
val skusList = listOf("premium_sub_month", "premium_sub_year", "some_inapp")
queryProductsForType(
skusList,
BillingClient.SkuType.SUBS
) { billingResult, skuDetailsList ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
val products = skuDetailsList ?: mutableListOf()
queryProductsForType(
skusList,
BillingClient.SkuType.INAPP
) { billingResult, skuDetailsList ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
products.addAll(skuDetailsList ?: listOf())
listener.onSuccess(products)
} else {
listener.onFailure(
Error(billingResult.responseCode, billingResult.debugMessage)
)
}
}
} else {
listener.onFailure(
Error(billingResult.responseCode, billingResult.debugMessage)
)
}
}
}
private fun queryProductsForType(
skusList: List<String>,
@BillingClient.SkuType type: String,
listener: SkuDetailsResponseListener
) {
onConnected {
billingClient.querySkuDetailsAsync(
SkuDetailsParams.newBuilder().setSkusList(skusList).setType(type).build(),
listener
)
}
}
private fun onConnected(block: () -> Unit) {
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
block()
}
override fun onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
})
}
override fun onPurchasesUpdated(billingResult: BillingResult, purchaseList: MutableList<Purchase>?) {
// here come callbacks about new purchases
}
}
Eso es todo por hoy. En los próximos artículos, te hablaremos de la implementación de la compra, las pruebas y el manejo de errores.
Further reading
Trends-insights
septiembre 6, 2022