Transacciones y comprobación en Xamarin.iOS

Restauración de transacciones pasadas

Si la aplicación admite tipos de producto que se pueden restaurar, debe incluir algunos elementos de la interfaz de usuario para permitir a los usuarios restaurar esas compras. Esta funcionalidad permite a un cliente agregar el producto a dispositivos adicionales o restaurarlo en el mismo dispositivo después de borrarse o de quitar y volver a instalar la aplicación. Se pueden restaurar los siguientes tipos de producto:

  • Productos no consumibles
  • Suscripciones de renovación automática
  • Suscripciones gratuitas

El proceso de restauración debe actualizar los registros que mantiene en el dispositivo para cumplir con sus productos. El cliente puede optar por restaurar en cualquier momento, en cualquiera de sus dispositivos. El proceso de restauración vuelve a enviar todas las transacciones anteriores para ese usuario; después, el código de aplicación debe determinar qué acción realizar con esa información (por ejemplo, comprobar si ya hay un registro de esa compra en el dispositivo y, si no es así, crear un registro de la compra y habilitar el producto para el usuario).

Implementación de la restauración

El botón Restaurar de la interfaz de usuario llama al siguiente método, que desencadena RestoreCompletedTransactions en SKPaymentQueue.

public void Restore()
{
   // theObserver will be notified of when the restored transactions start arriving <- AppStore
   SKPaymentQueue.DefaultQueue.RestoreCompletedTransactions();​​​
}

StoreKit enviará la solicitud de restauración a los servidores de Apple de forma asincrónica.

Dado que CustomPaymentObserver se registra como observador de transacciones, recibirá mensajes cuando respondan los servidores de Apple. La respuesta contendrá todas las transacciones que este usuario ha realizado en esta aplicación (en todos sus dispositivos). El código recorre en bucle cada transacción, detecta el estado Restaurado y llama al método UpdatedTransactions para procesarlo como se muestra a continuación:

// called when the transaction status is updated
public override void UpdatedTransactions (SKPaymentQueue queue, SKPaymentTransaction[] transactions)
{
   foreach (SKPaymentTransaction transaction in transactions)
   {
       switch (transaction.TransactionState)
       {
       case SKPaymentTransactionState.Purchased:
          theManager.CompleteTransaction(transaction);
           break;
       case SKPaymentTransactionState.Failed:
          theManager.FailedTransaction(transaction);
           break;
       case SKPaymentTransactionState.Restored:
           theManager.RestoreTransaction(transaction);
           break;
​default:
           break;
       }
   }
}

Si no hay ningún producto restaurable para el usuario, no se llamará a UpdatedTransactions.

El código más sencillo posible para restaurar una transacción determinada en el ejemplo realiza las mismas acciones que cuando se realiza una compra, excepto que la propiedad OriginalTransaction se usa para acceder al id. de producto:

public void RestoreTransaction (SKPaymentTransaction transaction)
{
   // Restored Transactions always have an 'original transaction' attached
   var productId = transaction.OriginalTransaction.Payment.ProductIdentifier;
   // Register the purchase, so it is remembered for next time
   PhotoFilterManager.Purchase(productId); // it's as though it was purchased again
   FinishTransaction(transaction, true);
}

Una implementación más sofisticada puede comprobar otras propiedades transaction.OriginalTransaction, como la fecha original y el número de recibo. Esta información será útil para algunos tipos de producto (como las suscripciones).

Finalización de la restauración

CustomPaymentObserver tiene dos métodos adicionales a los que llamará StoreKit cuando el proceso de restauración se haya completado (ya sea correctamente o con un error), que se muestran a continuación:

public override void PaymentQueueRestoreCompletedTransactionsFinished (SKPaymentQueue queue)
{
   Console.WriteLine(" ** RESTORE Finished ");
}
public override void RestoreCompletedTransactionsFailedWithError (SKPaymentQueue queue, NSError error)
{
   Console.WriteLine(" ** RESTORE FailedWithError " + error.LocalizedDescription);
}

En el ejemplo, estos métodos no hacen nada, pero una aplicación real puede optar por implementar un mensaje al usuario o a alguna otra funcionalidad.

Protección de compras

En los dos ejemplos de este documento se usa NSUserDefaults para realizar un seguimiento de las compras:

Consumibles: el "saldo" de las compras de crédito es un valor entero NSUserDefaults simple que se incrementa con cada compra.

No consumibles: cada compra de filtro de fotos se almacena como par clave-valor en NSUserDefaults.

El uso de NSUserDefaults mantiene sencillo el código de ejemplo, pero no ofrece una solución muy segura, ya que es posible que los usuarios con conocimientos técnicos actualicen la configuración (omitiendo el mecanismo de pago).

Nota: las aplicaciones reales deben adoptar un mecanismo seguro para almacenar contenido comprado que no esté sujeto a alteración por parte del usuario. Esto puede implicar el cifrado u otras técnicas, incluida la autenticación de servidor remoto.

El mecanismo también debe diseñarse para aprovechar las características de copia de seguridad y recuperación integradas de iOS, iTunes e iCloud. Esto garantizará que las compras anteriores de un usuario estén disponibles de forma inmediata una vez que este restaure una copia de seguridad.

Consulte la Guía de codificación segura de Apple para obtener instrucciones más específicas de iOS.

Comprobación de recibos y productos entregados por el servidor

Los ejemplos de este documento hasta ahora han consistido únicamente en la comunicación directa de la aplicación con los servidores de App Store para realizar transacciones de compra, lo que da lugar al desbloqueo de características o funcionalidades ya codificadas en la aplicación.

Apple proporciona un nivel adicional de seguridad de compra al permitir que otro servidor compruebe de forma independiente los recibos de compra, lo que puede ser útil para validar una solicitud antes de entregar contenido digital como parte de una compra (como un libro o una revista digital).

Productos integrados: al igual que los ejemplos de este documento, el producto que se compra existe como funcionalidad incluida con la aplicación. Una compra desde la aplicación permite al usuario acceder a la funcionalidad. Los identificadores de producto están codificados de forma rígida.

Productos entregados por el servidor: el producto consta de contenido descargable que se almacena en un servidor remoto hasta que una transacción correcta hace que se descargue el contenido. Algunos ejemplos podrían incluir libros o números de revistas. Normalmente, los identificadores de producto tienen su origen en un servidor externo (donde también se hospeda el contenido del producto). Las aplicaciones deben implementar una forma sólida de grabación cuando se haya completado una transacción, de modo que si se produce un error en la descarga de contenido, se pueda volver a intentar sin confundir al usuario.

Productos entregados por el servidor

Parte del contenido del producto, como libros y revistas (o incluso un nivel de juego), debe descargarse desde un servidor remoto durante el proceso de compra. Esto significa que se requiere un servidor adicional para almacenar y entregar el contenido del producto una vez comprado.

Obtención de precios de productos entregados por el servidor

Dado que los productos se entregan de forma remota, también es posible agregar más productos con el tiempo (sin actualizar el código de la aplicación), como agregar más libros o nuevos números de una revista. Para que la aplicación pueda detectar estos productos nuevos y mostrárselos al usuario, el servidor adicional debe almacenar y entregar esta información.

Getting Prices for Server-Delivered Products

  1. La información del producto debe almacenarse en varios lugares: en el servidor y en iTunes Connect. Además, cada producto tendrá archivos de contenido asociados. Estos archivos se entregarán después de una compra correcta.

  2. Cuando el usuario desea comprar un producto, la aplicación debe determinar qué productos están disponibles. Esta información se puede almacenar en caché, pero debe entregarse desde un servidor remoto donde se almacena la lista maestra de productos.

  3. El servidor devuelve una lista de identificadores de producto para que la aplicación se ocupe de su análisis.

  4. A continuación, la aplicación determina cuáles de estos identificadores de producto se van a enviar a StoreKit para recuperar precios y descripciones.

  5. StoreKit envía la lista de identificadores de producto a los servidores de Apple.

  6. Los servidores de iTunes responden con información válida del producto (descripción y precio actual).

  7. Al SKProductsRequestDelegate de la aplicación se le pasa la información del producto para mostrarla al usuario.

Compra de productos entregados por el servidor

Dado que el servidor remoto requiere alguna forma de validar que una solicitud de contenido es válida (es decir, que se ha pagado), la información del recibo se pasa para la autenticación. El servidor remoto reenvía esos datos a iTunes para su comprobación y, en caso de hacerlo correctamente, incluye el contenido del producto en la respuesta a la aplicación.

Purchasing Server-Delivered Products

  1. La aplicación agrega un SKPayment a la cola. Si es necesario, se le pedirá al usuario su id. de Apple y se le solicitará confirmar el pago.

  2. StoreKit envía la solicitud al servidor para su procesamiento.

  3. Una vez completada la transacción, el servidor responde con un recibo de transacción.

  4. La subclase SKPaymentTransactionObserver recibe el recibo y lo procesa. Dado que el producto debe descargarse desde un servidor, la aplicación inicia una solicitud de red al servidor remoto.

  5. La solicitud de descarga va acompañada de los datos del recibo para que el servidor remoto pueda comprobar que tiene autorización para acceder al contenido. El cliente de red de la aplicación espera una respuesta a esta solicitud.

  6. Cuando el servidor recibe una solicitud de contenido, analiza los datos del recibo y envía una solicitud directamente a los servidores de iTunes para comprobar que el recibo es para una transacción válida. El servidor debe usar cierta lógica para determinar si se debe enviar la solicitud a la dirección URL del espacio aislado o de producción. Apple sugiere usar siempre la dirección URL de producción y cambiar al espacio aislado si el estado 21007 (recibo de espacio aislado enviado al servidor de producción). Consulte la Guía de programación de validación de recibos de Apple para obtener más detalles.

  7. iTunes comprobará el recibo y devolverá el estado cero si es válido.

  8. El servidor espera la respuesta de iTunes. Si recibe una respuesta válida, el código debe localizar el archivo de contenido del producto asociado para incluirlo en la respuesta a la aplicación.

  9. La aplicación recibe y analiza la respuesta, guardando el contenido del producto en el sistema de archivos del dispositivo.

  10. La aplicación habilita el producto y, a continuación, llama a FinishTransaction de StoreKit. Después, la aplicación puede mostrar opcionalmente el contenido comprado (por ejemplo, mostrar la primera página de un libro o número revista comprado).

Una implementación alternativa para archivos de contenido del producto muy grandes podría implicar simplemente almacenar el recibo de transacción en el paso n.º 9 para que la transacción se pueda completar rápidamente y proporcionar una interfaz de usuario para que el usuario descargue el contenido del producto real en algún momento posterior. La solicitud de descarga posterior puede volver a enviar el recibo almacenado para acceder al archivo de contenido del producto necesario.

Escritura del código de verificación de recibos del lado servidor

La validación de un recibo en el código del lado servidor se puede realizar con una solicitud o respuesta HTTP POST sencilla que abarca los pasos n.º 5 al n.º 8 en el diagrama de flujo de trabajo.

Extraiga la propiedad SKPaymentTansaction.TransactionReceipt en la aplicación. Estos son los datos que se deben enviar a iTunes para su comprobación (paso n.º 5).

Codifique en base64 los datos del recibo de transacción (ya sea en el paso n.º 5 o en el n.º 6).

Cree una carga útil JSON sencilla como esta:

{
   "receipt-data" : "(base-64 encoded receipt here)"
}

Publique mediante HTTP POST el JSON en https://buy.itunes.apple.com/verifyReceipt para producción o https://sandbox.itunes.apple.com/verifyReceipt para pruebas.

La respuesta JSON contendrá las claves siguientes:

{
   "status" : 0,
   "receipt" : { (receipt repeated here) }
}

El estado cero indica un recibo válido. El servidor puede seguir cumpliendo con el contenido del producto comprado. La clave de recibo contiene un diccionario JSON con las mismas propiedades que el objeto SKPaymentTransaction recibido por la aplicación, por lo que el código de servidor puede consultar este diccionario para recuperar información como product_id y la cantidad de la compra.

Consulte la documentación de la Guía de programación de validación de recibos de Apple para obtener información adicional.