Tutoriales

Tutorial, compras in-app con StoreKit (IAP)

No le temas a las compras dentro de la aplicación

Resumen del artículo
  • Una de las formas de monetización que más dinero genera en el App Store son las compras dentro de la aplicación. Te enseñamos a cómo implementarlas en tu app.

Las compras dentro de la aplicación o In-App Purchases (IAP) son una forma más de monetizar una aplicación o servicio dentro del App Store.

Los usuarios suelen ser reticentes a la hora de pagar por una aplicación sin poder probarla antes, así que ofrecer características básicas de forma gratuita y añadir otras más avanzadas mediante un pago, puede ser una solución eficaz a la hora de monetizar nuestro trabajo.

Otra variante podrían ser las aplicaciones que proporcionan un servicio. Para este caso, Apple también ofrece una forma sencilla de gestionar las suscripciones tanto para desarrolladores como para los propios usuarios.

Tipos de compras In-App

Antes una advertencia: si quieres implementar las compras por tu cuenta, primero revisa si está permitido. Sólo hay unos pocos casos en los que Apple permite implementar una pasarela de pago fuera del App Store. Y por supuesto, conviene recordar que a cambio de proporcionar la plataforma Apple se queda una comisión por cada compra o por cada pago de las suscripciones. Pero utilizar las herramientas del SDK oficial simplifica el desarrollo y genera confianza a los compradores.

Apple se queda un 30% de las compras integradas, que en las suscripciones baja a un 15% cuando cualquier usuario lleve más de un año pagando nuestros servicio.

Los tipos de compras dentro de la aplicación que proporciona Apple son:

  • Consumibles que pueden utilizarse una vez y luego desaparecen. Podrán comprarse de nuevo si el usuario lo desea. Como las monedas en un juego.
  • No consumibles que se compran una vez y ya no caducan. Como acceder a funciones premium o nuevos niveles en un juego. Una vez compradas ya son para el usuario para siempre.
  • Suscripciones auto-renovables que tras el periodo que comprenda la suscripción se renuevan de forma automática a menos que el usuario cancele la compra. Existen opciones avanzadas que incluyen periodos de prueba.
  • Suscripciones no auto-renovables que no son renovadas de forma automática si no que el usuario puede volver a comprarlas de nuevo cuando caducan.

Todos los tipos están implementadas en StoreKit, el framework de Apple que debes añadir a tu proyecto si quieres ofrecer compras integradas en tu aplicación.

import StoreKit

Es muy importante entender algo: Apple no permite que ofrezcamos nuestras suscripciones o compras integradas fuera del App Store si el producto es digital. Si ofrecemos monedas en un juego, se deben ofrecer obligatoriamente desde el App Store. Si tenemos funciones que el usuario puede comprar en nuestra app (funciones premium) también tiene que hacerse por el App Store.

No obstante, si ofrecemos un servicio que sea de producto digital que se consuma directamente en la app, podemos ofrecer nuestro propio sistema de pago externo pero no podremos ofrecer al usuario darse de alta en nuestro sistema ni dirigirlo en forma alguna a nuestra propia pasarela. Es importante que os leáis bien las normas de Apple al respecto si no queréis encontraros con la app rechazada.

Comprendiendo el ciclo de StoreKit

Una de las partes más complicadas de implementar las compras dentro de la aplicación es entender cómo funciona el ciclo de compra. Enseguida veremos cómo utilizar el framework para controlar de forma adecuada el uso de éstas dentro de nuestras aplicaciones.

En algunos casos basta con realizar la compra y proporcionar acceso a la funcionalidad, pero en otros casos como las suscripciones renovables debemos confiar en algún servicio de backend externo o propio para validar el estado.

Veamos cómo funciona a nivel de código. Los elementos más interesantes de StoreKit y qué nos permitirán implementar nuestras compras In-App son los siguientes:

  • SKProduct representa cada uno de los productos que hemos dado de alta en nuestra aplicación.
  • SKPayment contiene la información de cada uno de los pagos que se realizan. Siempre va asociado a uno de los SKProduct que tenga nuestra aplicación disponibles.
  • SKPaymentQueue es el objeto que implementa la cola de los pagos que se han realizado en la aplicación. Es fundamental para el flujo de las compras.
  • SKProductsRequest y SKProductsResponse son respectivamente la petición y la respuesta cuando queremos conocer las compras de las que dispone nuestra aplicación.

Un vez que ya conocemos a los actores, vayamos a ver la película.

Implementación paso a paso

Lo primero es dar de alta el producto o suscripción en el App Store Connect. Entramos en la web, seleccionamos la aplicación y dentro del menú Prestaciones tenemos la opción de Compras dentro de la app.

En ella vemos el listado de compras disponibles y con el botón de «+» podemos añadir una nueva. Seleccionamos el tipo de compra que necesitamos y rellenamos la información necesaria para crearla.

Volvemos al código de la aplicación y lo más sencillo es crear una clase IAPManager, que utilice el patrón singleton, que nos permita gestionar las suscripciones de forma sencilla desde cualquier parte de nuestro código.

class IAPManager: NSObject {
    static let shared = IAPManager()
}

Lo primero que debemos implementar son las delegaciones SKRequestDelegate y SKProductsRequestDelegate que recogen la respuesta a la petición de las compras disponibles. Extendemos nuestra clase y la conformamos con dichos protocolos.

extension IAPManager: SKProductsRequestDelegate, SKRequestDelegate {
    // Recibe los productos
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {}
    
    // Es llamado cuando ocurre algún error
    func request(_ request: SKRequest, didFailWithError error: Error) {}
    
    // Se ejecuta cuando finaliza la petición
    func requestDidFinish(_ request: SKRequest) {}
}

A la implementación de la clase le añadimos un método para realizar la consulta:

func getProducts(productIdentifiers: Set<String> = []) {
    let request = SKProductsRequest(productIdentifiers: productIdentifiers)
    request.delegate = self
    request.start()
}

El método puede recibir como parámetro el identificador o identificadores de las compras o podría dejarse en blanco para solicitar información sobre todos los productos.

Crearemos en función del parámetro la solicitud, después asignaremos la propia clase como delegado y con start() comenzaremos la petición. El resultado llegará a uno de los tres métodos de la delegación, que en el caso de ir todo bien, el método de retorno será productRequest y podremos leer el array de productos en la propiedad products del SKProductsResponse.

De esta forma seremos capaces de, por ejemplo, listar los productos para que el usuario escoja el que quiere comprar con una simple llamada al método IAPManager.shared.getProducts(). Después guardaríamos los productos en una propiedad de IAPManager y los leeremos desde la vista que muestra una lista de los productos.

Una vez conocidos los productos disponibles, el siguiente paso es realizar la compra. Para ello crearemos otro método dentro del gestor de compras de la siguiente forma:

func buy(product: SKProduct, withUsername username: String = "") {
    //let payment = SKPayment(product: product)
    let payment = SKMutablePayment(product: product)
    payment.applicationUsername = username
    
    SKPaymentQueue.default().add(self)
    SKPaymentQueue.default().add(payment)
}

Tras crear el pago para un producto producto, lo añadimos a la cola del sistema. Hemos incluido una opción que puede resultar muy útil (aunque puedes ver comentada en el snippet la opción estándar) para usar un identificador interno de nombre de usuario.

Con esto evitamos la repetición de compras con dos perfiles diferentes. Recordemos que las compras con StoreKit se asocian al perfil de iCloud y somos nosotros los que debemos establecer la relación las cuentas propias de nuestra aplicación o plataforma.

El sistema operativo se encarga de mostrar al usuario la interfaz de compra

El resultado de la transacción se recibe en otro delegado que podemos implementar en una nueva extensión:

extension IAPManager: SKPaymentTransactionObserver {
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        
    }
}

Este método es llamado cada vez que una de las transacciones de la cola cambia de estado. En la propiedad transactions recibimos las transacciones que han cambiado su estado. El estado es una enumeración que se guarda en la propiedad transactionState de cada una de ellas. Veamos un ejemplo de cómo gestionar la respuesta:

transactions.forEach {
    switch $0.transactionState {
    case .purchasing: ()
    case .deferred: ()
    case .failed, .purchased, .restored: SKPaymentQueue.default().finishTransaction($0)
    }
}

Es fundamental finalizar la transacción llamando a la función finishTransaction en los casos indicados porque si no lo hacemos, seguirá en la cola la próxima vez que la consultemos y generará errores. La descripción de los estados es la siguiente:

  • purchasing indica que la compra sigue pendiente de ser procesa por el AppStore, debemos esperar a que cambie el estado y se vuelva llamar a la función para tomar una acción.
  • deferred se ha detenido la compra porque se debe tomar alguna acción externa como confirmar la compra cuando se usa control parental en el dispositivo. Se puede informar al usuario y esperar a que llegue otro evento.
  • failed la compra ha fallado, podemos consultar el error en la propiedad error de la transacción.
  • purchased la compra se ha realizado correctamente y ya podemos ejecutarla en nuestra aplicación o informar a la plataforma sobre la misma.
  • restored en el caso, que veremos a continuación, de haber restaurado una compra indica que se ha realizado de forma correcta y podemos ejecutar la acción pertinente como en el caso anterior.

Debemos tratar cada estado y realizar las acciones apropiadas. Recordemos que somos nosotros como desarrolladores los encargados de asegurar que el usuario obtiene el servicio o característica por el que acaba de pagar. En ciertos casos, como el cambio de terminal, Apple obliga a implementar un mecanismo que permita recuperar la compra.

Recuperación de compras

Si el usuario ha cambiado de terminal y sigue utilizando la misma cuenta de iCloud, puede recuperar una compra anterior si nuestra aplicación no usa un sistema de respaldo de las compras. Para ello, Apple permite utilizar StoreKit para consultar y restaurar las compras realizadas por el usuario anteriormente. Añadiremos un método para acceder a esta característica.

func restore(withUsername username: String? = nil) {
    SKPaymentQueue.default().add(self)
    SKPaymentQueue.default().restoreCompletedTransactions(withApplicationUsername: username)
}

Ahora podemos observar el uso del username que implementamos anteriormente. Podemos indicar que se recuperen únicamente las compras asociadas un identificador interno de usuario para que no se pueda volver a utilizar la compra para varias cuentas.

Es obligatorio que nuestra pantalla de compra tenga el botón «Restaurar compras» bien visible por el usuario, si no, la app será rechazada.

Esta función lo que hace es volver al estado restored todas las transacciones que se habían finalizado anteriormente y llama al método paymentQueue(updatedTransactions:) cuando se produce el cambio.

Suscripciones con renovación automática

En el caso de las suscripciones con renovación automática se requiere la implementación de un sistema de backend que verifique tanto la compra como el estado de las suscripción. Para ello debemos enviar al servidor la información del recibo de compra que encontramos en esta URL de nuestro terminal Bundle.main.appStoreReceiptURL.

La forma de validar que la compra y que los datos son correctos es llamando al endpoint en Apple, verifyReceipt desde el backend.

Importante esto último porque Apple no recomienda realizar la llamada desde las aplicaciones ya que no se puede garantizar una conexión segura al ser un terminal móvil y no controlar la red a la que puede estar conectado.

Apple permite a los usuarios gestionar las suscripciones desde el propio sistema

Como las suscripciones pueden cambiar en cualquier momento, tenemos que indicarle a Apple un endpoint en nuestro sistema de backend para que nos notifique de los cambios de estado de las suscripciones.

Esta URL se puede añadir desde App Store Connect en la información general de la aplicación rellenando el campo URL del estado de la suscripción. Ahora será responsabilidad del backend avisar a nuestra aplicación, cuando esta se ejecute, del estado actual para poder realizar las acciones necesarias.

Ya puedes implementar tus compras in-app

Aunque seguramente nos hemos dejado algunas cosas en el tintero, como pueden ser las opciones avanzadas de algunos tipos de compras y suscripciones, con este tutorial puedes comenzar a implementar compras básicas en tu app y has abierto el camino para explorar opciones más avanzadas.

Actualmente Apple ofrece opciones como promociones para el precio o en un periodo determinado, periodos de gracia antes de cancelar las suscripción, asignar una cantidad de ítems a una compra… y cada WWDC añade alguna forma más que intenta ofrecer nuevas soluciones tanto a los desarrolladores como a los usuarios.

Ya sólo nos queda desearte mucha suerte con tu nueva aplicación, y si tiene cualquier duda puedes dejarnos un comentario en este post o en nuestras redes sociales. Un saludo y Good Apple Coding.

Etiquetas

Arturo Rivas

Líder técnico especializado en mobile. Analista y desarrollador software. Apasionado de la tecnología, el deporte y la música.

Artículos relacionados

Botón volver arriba
Cerrar
Cerrar