
Hola a todos! Mi nombre es Denis, estoy desarrollando Apphud , un servicio para el análisis de suscripciones auto-renovables en aplicaciones iOS.
En este artículo, le diré cómo configurar, implementar y validar suscripciones auto-renovables en iOS 12 e iOS 13. Como beneficio adicional, le contaré sobre puntos sutiles y dificultades que no todos los desarrolladores tienen en cuenta.
Configurar suscripciones en App Store Connect
Si ya tiene una ID de paquete y una aplicación creada, puede omitir estos pasos. Si está creando una aplicación por primera vez, haga lo siguiente:
Debe crear un ID de paquete explícito (ID de aplicación) en el Portal de desarrolladores de Apple . Con una página llamada Certificados, identificadores y perfiles abiertos, vaya a la pestaña Identificadores . En junio de 2019, Apple finalmente actualizó el diseño del portal de acuerdo con ASC (abreviatura de App Store Connect).

Nuevo diseño para Apple Developer Portal en 2019

El ID explícito del paquete generalmente se especifica en el estilo de dominio ( com.apphud.subscriptionstest
). En la sección Capacidades , notará que la marca de verificación junto a Compras en la aplicación ya está marcada. Después de crear la ID del paquete ( ID de la aplicación ), vaya a App Store Connect.
Usuarios de prueba (usuarios de Sandbox)
Para probar futuras compras, deberá crear un usuario de prueba. Para hacer esto, vaya a ASC en la pestaña Usuarios y acceso , luego a Sandbox Testers.

Formulario de creación de sandbox de usuario
Al crear un probador, puede especificar cualquier dato inexistente, lo más importante, ¡no olvide el correo electrónico y la contraseña!
Hablaré sobre cómo probar las compras con credenciales de prueba cerca del final del artículo.
Otro paso importante es establecer contratos y datos bancarios en la sección " Acuerdos, impuestos y operaciones bancarias ". Si no tiene un acuerdo para las aplicaciones pagas, ¡no podrá probar las suscripciones auto-renovables!
Después de eso, puede crear una nueva aplicación en App Store Connect. Especifique un nombre único y seleccione su ID de paquete como ID del paquete.

La ID del paquete es su ID de paquete
Inmediatamente después de crear la aplicación, vaya a la pestaña Características.
Si ya ha creado la aplicación, puede continuar leyendo desde aquí.
El proceso de creación de una suscripción auto-renovable consta de varias etapas:
1. Cree un identificador de suscripción y cree un grupo de suscripción . Un grupo de suscripción es una colección de suscripciones con diferentes períodos y precios, pero que abren la misma funcionalidad en la aplicación. Además, en el grupo de suscripción, solo puede activar el período de prueba gratuito una vez, y solo una de las suscripciones puede estar activa. Si desea que su aplicación tenga dos suscripciones diferentes al mismo tiempo, deberá crear dos grupos de suscripciones.
2. Completar los datos de suscripción: duración, nombre para mostrar en la App Store (no debe confundirse con solo el nombre) y descripción. Si agrega la primera suscripción al grupo, deberá indicar el nombre para mostrar del grupo de suscripción. Recuerde guardar sus cambios con más frecuencia, ASC puede congelarse en cualquier momento y dejar de responder.

Pantalla de suscripción
3. Rellenar el precio de suscripción. Hay dos etapas: creación de precios y ofertas especiales. Indique el precio real en cualquier moneda, se recalcula automáticamente para todos los demás países. Ofertas introductorias: aquí puede ofrecer a los usuarios un período de prueba gratuito o descuentos prepagos. Las promociones aparecieron en la App Store recientemente en 2019: le permiten ofrecer descuentos especiales a los usuarios que se han dado de baja y a quienes desea devolver.
Generación de clave secreta compartida
En la página con una lista de todas sus suscripciones creadas, verá la clave compartida para el botón de la aplicación . Esta es una línea especial que se necesita para validar un cheque en una aplicación iOS. Tendremos que validar el cheque para determinar el estado de la suscripción.
La clave compartida puede ser de dos tipos: una clave única para su aplicación o una única clave para su cuenta. Importante: en ningún caso, vuelva a crear la clave si ya tiene la aplicación en la App Store, de lo contrario, los usuarios no podrán validar la verificación y su aplicación dejará de funcionar como se esperaba.

En este ejemplo, se crean tres grupos de suscripción y 3 suscripciones anuales.
Copie la ID de todas sus suscripciones y la clave compartida, esto será útil más adelante en el código.
Parte de software
Pasemos a la parte práctica. ¿Qué se necesita para hacer un gerente de compras completo? Como mínimo, se debe implementar lo siguiente:
Pago
Verificar estado de suscripción
Comprobar actualización
Recuperación de transacciones (¡no debe confundirse con actualizar un cheque!)
Todo el proceso de compra se puede dividir en 2 etapas: recibir productos (clase SKProduct
) e inicializar el proceso de compra (clase SKPayment
). En primer lugar, debemos especificar el delegado del protocolo SKPaymentTransactionObserver
.
La notificación IAP_PRODUCTS_DID_LOAD_NOTIFICATION
usa para actualizar la IU en una aplicación.
A continuación, escribimos un método para inicializar la compra:
func purchaseProduct(product : SKProduct, success: @escaping SuccessBlock, failure: @escaping FailureBlock){ guard SKPaymentQueue.canMakePayments() else { return } guard SKPaymentQueue.default().transactions.last?.transactionState != .purchasing else { return } self.successBlock = success self.failureBlock = failure let payment = SKPayment(product: product) SKPaymentQueue.default().add(payment) }
El delegado de SKPaymentTransactionObserver
tiene este aspecto:
extension IAPManager: SKPaymentTransactionObserver { public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { switch (transaction.transactionState) { case .purchased: SKPaymentQueue.default().finishTransaction(transaction) notifyIsPurchased(transaction: transaction) break case .failed: SKPaymentQueue.default().finishTransaction(transaction) print("purchase error : \(transaction.error?.localizedDescription ?? "")") self.failureBlock?(transaction.error) cleanUp() break case .restored: SKPaymentQueue.default().finishTransaction(transaction) notifyIsPurchased(transaction: transaction) break case .deferred, .purchasing: break default: break } } } private func notifyIsPurchased(transaction: SKPaymentTransaction) { refreshSubscriptionsStatus(callback: { self.successBlock?() self.cleanUp() }) { (error) in
Tras una suscripción exitosa, se llama al método delegado en el que la transacción tiene el estado purchased
.
Pero, ¿cómo determinar la fecha de vencimiento de una suscripción? Para hacer esto, haga una solicitud por separado a Apple.
Verificar estado de suscripción
La verificación se valida utilizando la solicitud de verificación de verifyReceipt
POST a Apple, enviamos la verificación cifrada como una cadena codificada en base64 como parámetro, y en la respuesta recibimos la misma verificación en formato JSON. En la matriz, la clave latest_receipt_info
enumerará todas las transacciones de cada período de cada suscripción, incluidos los períodos de prueba. Solo podemos analizar la respuesta y obtener la fecha de vencimiento actual de cada producto.
En WWDC 2017, agregaron la capacidad de recibir solo cheques actuales para cada suscripción utilizando la clave de exclude-old-transactions
en la solicitud de verifyReceipt
.
func refreshSubscriptionsStatus(callback : @escaping SuccessBlock, failure : @escaping FailureBlock){
Al comienzo del método, puede ver que hay un cheque por la existencia de una copia local del cheque. Es posible que no exista una verificación local, por ejemplo, si la aplicación se instaló a través de iTunes. Si no hay ninguna verificación, no podemos ejecutar la solicitud verifyReceipt
. Primero debemos obtener el cheque local actual y luego intentar validarlo nuevamente. La actualización de la verificación se realiza utilizando la clase SKReceiptRefreshRequest
:
private func refreshReceipt(){ let request = SKReceiptRefreshRequest(receiptProperties: nil) request.delegate = self request.start() } func requestDidFinish(_ request: SKRequest) {
La actualización de verificación se implementa en la función refreshReceipt()
. Si la verificación se actualiza correctamente, se requestDidFinish(_ request : SKRequest)
método delegado requestDidFinish(_ request : SKRequest)
, que llama refreshSubscriptionsStatus
método refreshSubscriptionsStatus
.
¿Cómo se implementa el análisis de la información de compra? Se nos devuelve un objeto JSON en el que hay una matriz anidada de transacciones (por la clave latest_receipt_info
). Revisamos la matriz, obtenemos la fecha de vencimiento utilizando la clave expires_date
y la expires_date
si esta fecha aún no ha llegado.
private func parseReceipt(_ json : Dictionary<String, Any>) {
Di un ejemplo simple de cómo extraer la fecha de vencimiento actual de una suscripción. No hay manejo de errores y, por ejemplo, no hay cheque para la devolución de una compra ( se agrega la fecha de cancelación ).
Para determinar si una suscripción está activa o no, solo compare la fecha actual con la fecha de Valores predeterminados del usuario por clave de producto. Si está ausente o es inferior a la fecha actual, la suscripción se considera inactiva.
func expirationDateFor(_ identifier : String) -> Date?{ return UserDefaults.standard.object(forKey: identifier) as? Date } let subscriptionDate = IAPManager.shared.expirationDateFor("YOUR_PRODUCT_ID") ?? Date() let isActive = subscriptionDate > Date()
La recuperación de la transacción se realiza en una sola línea SKPaymentQueue.default().restoreCompletedTransactions()
. Esta función restaura todas las transacciones completadas llamando func paymentQueue(**_** queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction])
método delegado func paymentQueue(**_** queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction])
.
¿Cuál es la diferencia entre actualizar un cheque desde la recuperación de la transacción?
Ambos métodos ayudan a restaurar sus datos de compra. ¿Pero cuáles son sus diferencias? Hay una maravillosa mesa con video wwdc :

Tabla de diferencias de dos formas de restaurar las compras de WWDC
En la mayoría de los casos, solo necesita usar SKReceiptRefreshRequest()
, porque solo estamos interesados en recibir un cheque para el cálculo posterior de la fecha de vencimiento.
En el caso de las suscripciones auto-renovables, las transacciones en sí mismas no nos interesan, por lo que es suficiente usar solo una actualización de cheques. Sin embargo, hay casos en los que necesita usar el método de recuperación de transacciones: si su aplicación descarga contenido después de la compra (contenido alojado por Apple) o si aún admite versiones inferiores a iOS 7.
Pruebas de compras (pruebas de sandbox)
Anteriormente, para probar las compras, tenía que iniciar sesión desde la App Store en la configuración de su iPhone. Esto fue muy inconveniente (por ejemplo, se borró toda la Biblioteca de Música de Apple). Sin embargo, esto no necesita hacerse ahora: la cuenta sandbox ahora existe por separado de la cuenta principal.

El proceso de compra es similar en comparación con las compras reales en la App Store, pero hay algunos puntos:
Siempre deberá ingresar la contraseña de inicio de sesión a través de la ventana del sistema. Las compras con Touch ID / Face ID aún no son compatibles.
Si, al ingresar el nombre de usuario y la contraseña correctamente, el sistema solicita una y otra vez la contraseña de inicio de sesión, haga clic en "Cancelar" , minimice la aplicación y vuelva a intentarlo. Parece una tontería, pero funciona para muchos. Pero a veces, después de la segunda entrada de contraseña, el proceso continúa.
No podrá probar el proceso de cancelación de la suscripción de ninguna manera.
La duración de los períodos de suscripción es mucho menor que la real. Y se actualizan no más de 6 veces al día.
¿Qué hay de nuevo en StoreKit en iOS 13?
De la nueva, solo la clase SKStorefront
, que proporciona información sobre en qué país está registrado el usuario en la App Store. Esto puede ser útil para aquellos desarrolladores que usan diferentes suscripciones para diferentes países. Anteriormente, todos verificaban por geolocalización o por región del dispositivo, pero esto no daba un resultado preciso. Ahora es muy fácil encontrar el país en la App Store: SKPaymentQueue.default().storefront?.countryCode
. También se agregó un delegado de método si el país en la App Store cambió durante el proceso de compra. En este caso, puede continuar o cancelar el proceso de compra usted mismo.
Errores al trabajar con suscripciones
- Apple no recomienda verificar un cheque directamente desde un dispositivo. Hablaron sobre esto varias veces en WWDC (desde las 5:50) y esto se indica en la documentación . Esto no es seguro porque un atacante puede interceptar datos mediante un ataque de hombre en el medio. La forma correcta de verificar las comprobaciones es la validación local utilizando su servidor.
- Hay un problema al verificar la fecha de vencimiento. Si no está utilizando su servidor, entonces la hora del sistema en el dispositivo puede cambiarse a una más antigua, y luego nuestro código dará el resultado incorrecto: la suscripción se considerará activa. Si esto no le conviene, puede utilizar cualquier servicio que emita una hora mundial precisa.
- No todos los usuarios pueden tener una prueba gratuita. El usuario podría volver a instalar la aplicación después de un tiempo, y la aplicación mostrará que la versión de prueba está disponible como de costumbre. Será correcto actualizar la verificación, validarla y verificar en JSON la disponibilidad de la prueba para este usuario. Muchos no lo hacen.
- Si el usuario solicitó un reembolso,
cancellation_date
se agregará a la suscripción JSON, pero expires_date
permanecerá sin cambios. Por lo tanto, es importante verificar siempre la presencia del campo cancellation_date
, que es preferible a expires_date
. - No vale la pena actualizar el cheque cada vez que se inicia la aplicación, porque, en primer lugar, esto no tiene sentido y, en segundo lugar, lo más probable es que se le presente al usuario una ventana de entrada de contraseña de Apple ID. Vale la pena actualizar un cheque, por ejemplo, cuando el propio usuario hizo clic en el botón de restauración de compras.
- ¿Cómo determinar en qué puntos vale la pena validar un cheque para obtener la fecha de vencimiento actual de una suscripción? Puede validar el cheque en cada inicio, o solo al final de la suscripción. Sin embargo, si verifica el cheque solo al final de la suscripción, el usuario que haya emitido el reembolso podrá usar su aplicación de forma gratuita hasta el final del período.
Conclusión
Espero que este artículo te sea útil. Intenté agregar no solo el código, sino también explicar los puntos sutiles en el desarrollo. El código completo de la clase se puede descargar aquí . Esta clase será muy útil para los desarrolladores principiantes y aquellos que quieran aprender más sobre cómo funciona todo. Para aplicaciones en vivo, se recomienda utilizar soluciones más serias, por ejemplo, SwiftyStoreKit .
¿Desea implementar suscripciones en su aplicación iOS en 10 minutos? Integra Apphud y:
- Haga compras usando solo un método;
- rastrea automáticamente el estado de la suscripción de cada usuario;
- Integre ofertas de suscripción fácilmente
- enviar eventos de suscripción a Amplitude, Mixpanel, Slack y Telegram teniendo en cuenta la moneda local del usuario;
- disminuir la tasa de abandono en las aplicaciones y devolver a los usuarios no suscritos.