Recibos de Compra
Extensiones de la Store (tienda)

Validación de Recibo

Usted puede utilizar la validación de recibo para que sea más difícil que los hackers obtengan acceso a contenido sin pagar por ello.

Dónde validar

Dónde usted coloque una lógica de validación depende en los requerimientos de su aplicación, pero una regla general es que una validación de recibo debería ser realizada en algún punto dónde el contenido es dispensado.

Por ejemplo, si usted está vendiendo contenido que ya estaba bundled (agrupado) dentro de su aplicación, su aplicación debería tener toda la lógica necesaria para decidir si un jugador tiene derecho a ella.

Sin embargo, si usted está vendiendo contenido que es entregado por un servidor, tal como contenido descargable o características multo-jugador, el servidor es un lugar natural para aplicar la validación.

 Validación local

En este escenario, nosotros estamos vendiendo contenido que ya existe en el dispositivo; su aplicación simplemente toma una decisión acerca si desbloquearla o no.

El Unity IAP proporciona herramientas para ayudar a ocultar secretos, validar y parse recibos en las tiendas de Google y Apple.

 Ofuscar secretos

La validación de recibo es realizada utilizando unas claves de cifrado; la key (clave) pública de Google Play de su aplicación y/o el certificado root de Apple suyo.

Si un intruso puede remplazar estos secretos entonces serán capaces de vencer sus revisiones de validación de recibos, por lo que es importante hacer que sea difícil que un intruso pueda fácilmente encontrar y modificar estas llaves.

El Unity IAP proporciona una herramienta que le puede ayudar a ofuscar sus claves secretas dentro de su aplicación; la ventana del Obfuscator (obfuscador) es accesible vía Window > Unity IAP > IAP Receipt Validation Obfuscator.

La ventana obfuscator
La ventana obfuscator

This window will encode both Apple’s root certificate, which is bundled with Unity IAP, and your Google Play public key, into two different C# files that are added to your project, GooglePlayTangle and AppleTangle, for use in the next section.

Note that you do not have to provide a Google Play public key if you are only targeting Apple’s stores, or vice versa.

Validating receipts

The CrossPlatformValidator class can be used for validation across both Google Play and Apple stores.

You must supply this class with either your Google Play public key or Apple’s root certificate, or both if you wish to validate across both platforms.

The checks performed by the CrossPlatformValidator are as follows:

  1. Receipt authenticity is checked via signature validation
  2. Receipt Application bundle identifier is compared to your Application’s, with an InvalidBundleId exception thrown if they do not match

Note that the validator will only validate receipts generated on Google Play and Apple platforms. Receipts generated on any other platform, including the fakes generated in the Editor, will throw an IAPSecurityException.

If you attempt to validate a receipt for a platform for which you have not supplied the required secret, a MissingStoreSecretException will be thrown.

public PurchaseProcessingResult ProcessPurchase (PurchaseEventArgs e)
{
    bool validPurchase = true; // Presume valid for platforms with no R.V.

    // Unity IAP's validation logic is only included on these platforms.
#if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
    // Prepare the validator with the secrets we prepared in the Editor
    // obfuscation window.
    var validator = new CrossPlatformValidator(GooglePlayTangle.Data(),
        AppleTangle.Data(), Application.bundleIdentifier);

    try {
        // On Google Play, result will have a single product Id.
        // On Apple stores receipts contain multiple products.
        var result = validator.Validate(e.purchasedProduct.receipt);
        // For informational purposes, we list the receipt(s)
        Debug.Log("Receipt is valid. Contents:");
        foreach (IPurchaseReceipt productReceipt in result) {
            Debug.Log(productReceipt.productID);
            Debug.Log(productReceipt.purchaseDate);
            Debug.Log(productReceipt.transactionID);
        }
    } catch (IAPSecurityException) {
        Debug.Log("Invalid receipt, not unlocking content");
        validPurchase = false;
    }
#endif

    if (validPurchase) {
        // Unlock the appropriate content here.
    }

    return PurchaseProcessingResult.Complete;
}

It is important you check not just that the receipt is valid, but what information it contains.

A common attack vector is for hackers to supply receipts from other products or Applications; the receipts are genuine so will pass validation, so you should make decisions based on the product IDs parsed by the CrossPlatformValidator.

Store specific details

Different stores have different fields in their purchase receipts. To access store specific fields, IPurchaseReceipt can be downcast to two different subtypes, GooglePlayReceipt and AppleInAppPurchaseReceipt.

var result = validator.Validate(e.purchasedProduct.receipt);
Debug.Log("Receipt is valid. Contents:");
foreach (IPurchaseReceipt productReceipt in result) {
    Debug.Log(productReceipt.productID);
    Debug.Log(productReceipt.purchaseDate);
    Debug.Log(productReceipt.transactionID);

    GooglePlayReceipt google = productReceipt as GooglePlayReceipt;
    if (null != google) {
        Debug.Log(google.purchaseState);
        Debug.Log(google.purchaseToken);
    }

    AppleInAppPurchaseReceipt apple = productReceipt as AppleInAppPurchaseReceipt;
    if (null != apple) {
        Debug.Log(apple.originalTransactionIdentifier);
        Debug.Log(apple.cancellationDate);
        Debug.Log(apple.quantity);
    }
}

Parsing raw Apple receipts

The AppleValidator class can be used to extract detailed information about an Apple receipt. Note that this class only works with iOS 7+ style App receipts, not Apple’s deprecated transaction receipts.

#if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
// Get a reference to IAppleConfiguration during IAP initialization.
var appleConfig = builder.Configure<IAppleConfiguration>();
var receiptData = System.Convert.FromBase64String(appleConfig.appReceipt);
AppleReceipt receipt = new AppleValidator(AppleTangle.Data()).Validate(receiptData);

Debug.Log(receipt.bundleID);
Debug.Log(receipt.receiptCreationDate);
foreach (AppleInAppPurchaseReceipt productReceipt in receipt.inAppPurchaseReceipts) {
    Debug.Log(productReceipt.transactionIdentifier);
    Debug.Log(productReceipt.productIdentifier);
}
#endif

The AppleReceipt type models Apple’s ASN1 receipt format; see their documentation for an explanation of its fields.

Recibos de Compra
Extensiones de la Store (tienda)