Report: State of in-app subscriptions in the US 2023 Get a report

Novedades de la API de StoreKit 2 y cómo Apple ha simplificado la integración de las compras dentro de la aplicación

Kirill Potekhin

Updated: marzo 20, 2023

Content

62fdf0da8b35ff3ba074dbce jp android tutorial 1 configuration 5

Durante la Conferencia Mundial de Desarrolladores de Apple (WWDC) 2021 que tuvo lugar recientemente, Apple presentó una nueva versión de StoreKit 2. Se trata de un framework responsable de las compras en iOS. La proporción de aplicaciones (app) con funciones de compra y suscripción (subscription) dentro de la aplicación crece constantemente, y Apple ha simplificado significativamente la integración de las compras dentro de la aplicación con el lanzamiento de StoreKit 2. Hoy analizaremos el trabajo con StoreKit 2 en la parte del servidor, es decir, con la ayuda de App Store Server API.

Solicitar autenticación

En la versión actual de la API, necesitas el secreto compartido (shared secret) para enviar una solicitud. Se trata de una cadena secreta fija que puedes obtener en App Store Connect. La nueva versión de la API utiliza el estándar JSON Web Token (JWT) para la autenticación de las solicitudes.   

Generación de clave 

En primer lugar, crea una clave privada que se utilizará para autorizar las solicitudes. Abre App Store Connect y ve a la sección Users y Access, y a continuación a la pestaña Keys. Selecciona el tipo de clave de compras dentro de la aplicación. Descarga una nueva clave. También necesitarás su ID: puedes copiarlo en la misma página que el ID de la compra dentro de la aplicación, que se encuentra en la pestaña de la API de App Store Connect.

Creating a private key to work with StoreKit 2
Crear una clave privada para trabajar con la API de App Store Server

Crear un token

El siguiente paso es crear un token que se utilizará para autorizar las solicitudes. Este proceso se describe detalladamente en la documentación, así que no hay razón para prestarle demasiada atención. Aquí tienes un ejemplo de una implementación ya hecha para Python. Cabe destacar que no tiene sentido generar un nuevo token para cada nueva solicitud. Al crear un token, establece su vida útil en un máximo de 60 minutos y utiliza el mismo token durante este periodo. 

import time, uuid
from authlib.jose import jwt

BUNDLE_ID = 'com.adapty.sample_app'
ISSUER_ID = '4336a124-f214-4d40-883b-6db275b5e4aa'
KEY_ID = 'J65UYBDA74'
PRIVATE_KEY = '''
-----BEGIN PRIVATE KEY-----
MIGTAgMGByqGSMBHkAQQgR/fR+3Lkg4...
-----END PRIVATE KEY-----
'''

issue_time = round(time.time())
expiration_time = issue_time + 60 * 60 # 1 hour expiration


header = {
 'alg': 'ES256',
 'kid': KEY_ID,
 'typ': 'JWT'
}

payload = {
 'iss': ISSUER_ID,
 'iat': issue_time,
 'exp': expiration_time,
 'aud': 'appstoreconnect-v1',
 'nonce': str(uuid.uuid4()),
 'bid': BUNDLE_ID
}

token_encoded = jwt.encode(header, payload, PRIVATE_KEY)
token_decoded = token_encoded.decode()

authorization_header = {
 'Authorization': f'Bearer {token_decoded}'
}

Transacciones firmadas

En una nueva versión de la API, todas las transacciones se devuelven en el estándar JSON Web Signature (JWS). Se trata de una cadena formada por tres partes divididas por puntos.  

  1. Encabezado Base64.
  2. Carga útil de la transacción Base64.
  3. Firma de la transacción.
Base64(header) + "." + Base64(payload) + "." + sign(Base64(header) + "." + Base64(payload))

Encabezado de la transacción

Se necesita un encabezado para asegurarse de que la transacción es auténtica. La clave Alg contiene un algoritmo de encriptación, la clave x5c contiene una cadena de certificados.

{
  "kid": "AMP/DEV",
  "alg": "ES256",
  "x5c": [
    "MIIEO...",
    "MIIDK..."
  ]
}

Carga útil de la transacción

{
  "transactionId": "1000000831360853",
  "originalTransactionId": "1000000806937552",
  "webOrderLineItemId": "1000000063561721",
  "bundleId": "com.adapty.sample_app",
  "productId": "basic_subscription_1_month",
  "subscriptionGroupIdentifier": "27636320",
  "purchaseDate": 1624446341000,
  "originalPurchaseDate": 1619686337000,
  "expiresDate": 1624446641000,
  "quantity": 1,
  "type": "Auto-Renewable Subscription",
  "appAccountToken": "fd12746f-2d3a-46c8-bff8-55b75ed06aca",
  "inAppOwnershipType": "PURCHASED",
  "signedDate": 1624446484882,
  "offerType": 2,
  "offerIdentifier": "basic_subscription_1_month.pay_as_you_go.3_months"
}

Apple ha cambiado y ampliado el formato de las transacciones. Desde mi punto de vista, ahora es más cómodo trabajar con ellos. Puedes conocer los detalles del nuevo formato en la documentación. A continuación, describiré los cambios más importantes.    

  • Apple ha añadido el campo appAccountToken, que contiene el ID de usuario de tu sistema. Este ID debe estar en formato UUID, se establece en la aplicación móvil cuando se está inicializando una compra. Si se establece, se devolverá en todas las transacciones de esta cadena (renovación, problemas de facturación, etc.), y entenderás fácilmente qué usuario ha realizado una compra.    
  • Apple también ha añadido los campos offerType y offerIdentifier que contienen la información sobre una oferta usada (si la hay). Estos son los valores del campo offerType:
  • 1 – oferta de introducción (intro offer) (disponible sólo para los usuarios sin suscripciones activas o vencidas)
  • 2 – oferta promocional (promo offer) (disponible sólo para las suscripciones actuales y vencidas)
  • 3 – código de oferta

Si se ha utilizado una oferta promocional o un código de oferta, la clave offerIdentifier contendrá el ID de la oferta utilizada. Antes, era imposible hacer un seguimiento del uso de la oferta en el lado del servidor (server side), lo que empeoraba los análisis. Ahora, puedes utilizar los códigos de oferta para los análisis.   

  • Apple ha añadido el campo inAppOwnershipType, que ayuda a entender si un usuario ha comprado un producto o ha accedido a él gracias a una suscripción familiar. Valores posibles:
  • PURCHASED
  • FAMILY_SHARED
  • Otro campo nuevo -tipo- incluye el tipo de transacción. Valores posibles:
  • Suscripción autorenovable
  • No consumible
  • Consumible
  • Suscripción no renovable
  • Los campos Cancellation_date y cancellation_reason tienen ahora nuevos nombres: revocationDate y revocationReason. A modo de recordatorio, incluyen una fecha y un motivo de revocación de suscripción (subscription revocation) como resultado de un reembolso, por lo que el nuevo nombre parece más lógico.
  • Todas las claves se devuelven en formato camelCase (al igual que en todas las solicitudes de la API del servidor de App Store).
  • Todas las fechas se muestran en formato Unix timestamp en milisegundos.   

Subscribe to Adapty newsletter

Get fresh paywall ideas, subscription insights, and mobile app news every month!

Estado de la suscripción del usuario

Para comprobar el estado de la suscripción del usuario actual, envía una solicitud GET a https://api.storekit.itunes.apple.com/inApps/v1/subscriptions/{originalTransactionId}, donde {originalTransactionId} es el ID de cualquier cadena de transacciones del usuario. A cambio, obtendrás las transacciones con los estados de cada grupo de suscripciones.

{
  "environment": "Sandbox",
  "bundleId": "com.adapty.sample_app",
  "data": [
    {
      "subscriptionGroupIdentifier": "39636320",
      "lastTransactions": [
        {
          "originalTransactionId": "1000000819078552",
          "status": 2,
          "signedTransactionInfo": "eyJraWQiOi...",
          "signedRenewalInfo": "eyJraWQiOi..."
        }
      ]
    }
  ]
}

La clave de estado muestra el estado actual de la suscripción, en base a ella, puedes decidir si debes proporcionar a un usuario acceso a las funciones de pago de la aplicación. Valores posibles:

  • 1 – la suscripción está activa, el usuario debe poder acceder a las funciones de pago.
  • 2 – la suscripción ha vencido, el usuario no debe poder acceder a las funciones de pago.  
  • 3 – el estado de la suscripción es Reintento de Facturación (Billing Retry), lo que significa que un usuario no la ha cancelado, pero tiene problemas para pagar. Apple intentará cargar la tarjeta por 60 días. Un usuario no debe poder acceder a las funciones de pago.  
  • 4 – el estado de la suscripción es Periodo de Gracia (grace period), lo que significa que un usuario no la ha cancelado, pero tiene problemas de pago. El Periodo de Gracia está activado en App Store Connect, por lo que el usuario debe poder acceder a las funciones de pago.    
  • 5 – la suscripción ha sido cancelada como resultado de un reembolso, el usuario no debe poder acceder a las funciones de pago. 

La clave SignedTransactionInfo contiene la información sobre la última transacción de la cadena. Más arriba encontrarás los detalles sobre su formato.   

Información sobre la renovación de la suscripción (subscription renewal)  

La clave SignedRenewalInfo contiene la información sobre la renovación de la suscripción.

{
  "expirationIntent": 1,
  "originalTransactionId": "1000000819078552",
  "autoRenewProductId": "basic_subscription_1_month",
  "productId": "basic_subscription_1_month",
  "autoRenewStatus": 0,
  "isInBillingRetryPeriod": false,
  "signedDate": 1624520884048
}

Esta información nos permite saber qué ocurrirá con la suscripción durante el siguiente periodo de pago. Por ejemplo, si ves que un usuario ha cancelado la renovación automática, puedes ofrecerle cambiar a otro plan de suscripción o proporcionarle una oferta promocional. Es conveniente hacer un seguimiento de este tipo de eventos (events) con la ayuda de las notificaciones del servidor (server notifications), de las que te hablaré pronto.  

Historial de transacciones del usuario

Para obtener el historial de transacciones del usuario, envía una solicitud GET a https://api.storekit.itunes.apple.com/inApps/v1/history/{originalTransactionId}, donde {originalTransactionId} es el ID de cualquier cadena de transacciones del usuario. A cambio, obtendrás una matriz de transacciones ordenadas por tiempo.

{
	"revision": "1625872984000_1000000212854038",
	"bundleId": "com.adapty.sample_app",
	"environment": "Sandbox",
	"hasMore": true,
	"signedTransactions": [
		"eyJraWQiOiJ...",
		"joiRVMyNeyX...",
		"5MnkvOTlOZl...",
		...
	]
}

Una solicitud no puede contener más de 20 transacciones. Si un usuario tiene más, el valor de la marca hasMore será verdadero. Si necesitas la siguiente página de transacciones, envía de nuevo la solicitud con el parámetro GET de revisión conteniendo. Este contendrá el valor de la misma clave.  

Notificaciones de transacciones del servidor

Las notificaciones del servidor ayudan a obtener información sobre nuevas compras, renovaciones, problemas de facturación, etc. Esto ayuda a crear un análisis más preciso, así como a simplificar la gestión del estado del suscriptor.   

Las notificaciones del servidor existentes (V1) pueden resolver la mayoría de los problemas, pero a veces son inconvenientes. Principalmente, se trata de la situación en la que se reciben varias notificaciones por una sola acción de un usuario. Por ejemplo, ahora, al actualizar un usuario una suscripción, Apple envía dos notificaciones DID_CHANGE_RENEWAL_STATUS y INTERACTIVE_RENEWAL. Para procesar este caso actualmente, tienes que guardar el estado de alguna manera y comprobar si se ha enviado la segunda notificación. En la nueva versión de las notificaciones del servidor (V2), sólo hay una notificación para una acción de un usuario. Esto es mucho más conveniente.   

La segunda versión de las notificaciones al servidor presenta nuevos eventos: OFFER_REDEEMED, EXPIRED y GRACE_PERIOD_EXPIRED. Facilitan la gestión del estado de los suscriptores. Los eventos SUBSCRIBED y PRICE_INCREASE son eventos mejorados de la primera versión.

Transaction notifications in StoreKit 2 feature new events

Tipos de notificación

Ahora las notificaciones tienen tipos, por lo que basta con una notificación para cualquier acción de un usuario para entender lo que ha ocurrido. 

Notifications in StoreKit 2 have types

Tipos de notificación

{
  "notificationType": "SUBSCRIBED",
  "subtype": "INITIAL_BUY",
  "version": 2,
  "data": {
    "environment": "Sandbox",
    "bundleId": "com.adapty.sample_app",
    "appAppleId": 739104078,
    "bundleVersion": 1,
    "signedTransactionInfo": "eyJraWQiOi...",
    "signedRenewalInfo": "eyJraWQiOi..."
  }
}

Las notificaciones del servidor contienen información sobre una transacción y una renovación en el formato JWS que he descrito anteriormente.  

Trabajar con un entorno Sandbox (Sandbox)

Tienes que utilizar la URL del entorno Sandbox para probar las compras: https://api.storekit-sandbox.itunes.apple.com.

La nueva versión de las notificaciones del servidor aún no está disponible para ser probada. Una vez que esté disponible, será posible especificar diferentes URL para las notificaciones de Producción y Sandbox. Puedes elegir la V2 para Sandbox, y la V1 para Producción para las pruebas. 

Además, App Store Connect permite ahora:

  • Borrar el historial de compras para el usuario del Sandbox, lo que significa que ya no tienes que crear una nueva cuenta para hacerlo.   
  • Cambiar el país de la tienda para el usuario del Sandbox.
  • Cambiar el periodo de renovación de la suscripción de Sandbox, por ejemplo, puedes hacer una compra mensual que dure 1 hora en lugar de 5 minutos.     

Conclusión

Apple ha mejorado significativamente el trabajo con las compras dentro de la aplicación y las suscripciones en la app en el lado del servidor. Desde mi punto de vista, estas son las novedades más útiles:

  • Soporte completo de la oferta promocional y del código de oferta;  
  • Notificaciones del servidor más sencillas e informativas;   
  • Una oportunidad para conocer el estado de la suscripción actual con una simple llamada a la API;
  • Borrar el historial de compras del Sandbox del usuario.   

Cambiar a una nueva API no será difícil, basta con obtener el originalTransactionId para cada recibo. Es probable que ya esté contenida en tu base.    

En todo caso, lo más difícil al integrar las suscripciones en una aplicación móvil es crear un sistema de análisis y optimizar la economía. Adapty puede solucionar bien estos problemas:

  • Los análisis integrados nos permiten comprender rápidamente las métricas de la aplicación principal.
  • El análisis de cohortes (cohort analysis) ayuda a comprender si hay algún problema con la economía.
  • Las pruebas A/B (A/B testing) aumentan la rentabilidad de la aplicación.   
  • Las integraciones con sistemas externos permiten enviar las transacciones a los servicios de atribución y análisis del producto.    
  • Las campañas de promoción reducen la pérdida de audiencia.   
  • El SDK de código abierto permite integrar en pocas horas las suscripciones en una aplicación.  

Más información sobre estas funciones, para implementar las suscripciones en tu aplicación más rápidamente y monetizar tu aplicación antes y mejor.