IAP Validation

Introduction

Cheaters and invalid IAP transactions can cause Adjust & Looker dashboards to display inaccurate values and the UA networks to train their algorithms on bad data. This can lead to the contamination of UA algorithms’ decision-making process and incorrect game design choices.

LionSDK offers static methods for IAP receipt validation to help reduce or eliminate the negative impacts of cheaters’ IAP transactions.

Behind the scenes, LionSDK uses the Nakama SDK from Heroic Labs to help validate and track IAP receipts.

What does the package do?

  • Fire Adjust iap_purchase event to Adjust ONLY if the purchase is valid. This event displays revenue on Adjust dashboards and is passed to Networks as an important data point.
  • Fire inapp_purchase event to Snowflake (Looker) for valid and invalid(cheater) transactions. A parameter of the event will declare the validity of the purchases. This helps internally with monitoring; Looker dashboards will automatically filter out invalid transactions.

Requirements

Setup the Game on our Server

Based on the game status, the requirements differ:

  • If the game is hosted on Lion Studios’ Google Play / App Store accounts, contact your Product Manager so that the Product Manager can collaborate with the QA team to retrieve the credentials and complete the game setup.

  • If the game is hosted on your own Google Play / App Store account, You can follow the steps below for the respective platforms,

    Deliver these keys to your Product Manager so that they can complete the setup.

Remove Events (if applicable)

LionSDK will automatically fire the required Adjust and Analytics IAP events. In your Unity Project, you should remove these events from your game code to prevent duplication and overcounting:

  • Remove any existing Adjust IAP-related events (iap_purchase, purchase_failedpurchase_unknown and purchase_notverified)
  • Remove any existing calls to LionAnalytics.InAppPurchase

Remove Adjust Purchase Verification package (if applicable)

If it is installed in your project, delete the Adjust Purchase SDK.

Implementation

Call IAPValidation.ValidateAndLog()

If you use Unity Purchasing, you typically want to call IAPValidation.ValidateAndLog() in your ProcessPurchase method. This will send your receipt to our remote receipt validation service and log the results to both Lion Analytics and Adjust.

Parameters:

  • Product product: this is the UnityEngine.Purchasing.Product object that Unity Purchasing provides in the ProcessPurchase function’s PurchaseEventArgs parameter’s purchasedProduct field.

  • IAPGameplayInfo iapGameplayInfo: This object contains information required by LionAnalytics, including details about the in-game rewards. These include:

    • List ReceivedItems
    • List ReceivedCurrencies
    • String PurchaseLocation
  • Action onSuccess (optional): This callback, if provided, will be raised if the receipt is valid.

  • Action onFailure (optional): This callback, if provided, will be raised if the receipt has not been validated. The ValidationStatus will provide the reason for the failure to validate.

Examples:


Simple inline call:

using LionStudios.Suite.Purchasing;

IAPValidation.ValidateAndLog(
    purchaseEventArgs.purchasedProduct, 
    new IAPGameplayInfo(
        new List<Item>()
        {
            new Item("NoAds", 1),
            new Item("SpecialWeapon", 1),
            new Item("BonusCards", 10)
        },
        new List<VirtualCurrency>()
        {
            new VirtualCurrency("coins", "normal", 10000),
            new VirtualCurrency("gems", "special", 10)
        },
        "shop"));

Full ProcessPurchase example:

using LionStudios.Suite.Purchasing;

PurchaseProcessingResult IStoreListener.ProcessPurchase(PurchaseEventArgs purchaseEventArgs)
{
    /// Your code

    Product product = purchaseEventArgs.purchasedProduct;
    IAPGameplayInfo gameplayInfo;
    switch (product.definition.id)
    {
        case "com.company.game.noads":
            gameplayInfo = new IAPGameplayInfo(
                new List<Item>(){ new Item("NoAds", 1) }, 
                new List<VirtualCurrency>(), 
                "ingame");
            break;
        case "com.company.game.noadsspecial":
            gameplayInfo = new IAPGameplayInfo(
                new List<Item>(){ new Item("NoAds", 1) }, 
                new List<VirtualCurrency>() { new VirtualCurrency("coins", "normal", 1000) }, 
                "specialpopup");
            break;
        case "com.company.game.coinpack1":
            gameplayInfo = new IAPGameplayInfo(
                new List<Item>(), 
                new List<VirtualCurrency>() { new VirtualCurrency("coins", "normal", 1000) }, 
                "iapshop");
            break;
        case "com.company.game.coinpack2":
            gameplayInfo = new IAPGameplayInfo(
                new List<Item>(), 
                new List<VirtualCurrency>() { new VirtualCurrency("coins", "normal", 5000) }, 
                "iapshop");
            break;
        default:
            gameplayInfo = new IAPGameplayInfo(null, null, "unknown");
            break;
    }

    IAPValidation.ValidateAndLog(product, gameplayInfo);
    
    // Your code for rewarding the player
    
}

Conditional reward

In some rare cases, you’ll want to reward the player only if the receipt is valid. This is not normal behavior and should be reserved for multiplayer games where cheaters can harm the experience of other players.

In this case, you will do the same as the previous example except for the last lines:

IAPValidation.ValidateAndLog(
		product, 
		gameplayInfo, 
		() => GiveReward(product.definition.id), 
		DisplayError);

void GiveReward(string productId)
{
    // Your code for rewarding the player
}

void DisplayError(ValidationStatus status)
{
    // Your code for displaying an error message to the player
}

Validation

If the package is implemented correctly, for each purchase, two events will be fired:

  • inapp_purchase: a LionAnalytics event used for analytics on Looker.
  • iap_purchase: a native Adjust event used by Adjust for revenue metrics.

Make sure that these events are received by following either of the paths below:

  • If you have access to Looker, for each relevant event, check the following:

    • The event name appears [ref]
  • If you do not have access to Looker, please use the LionAnalytics QA Tool following the instructions here: Event Validation

In Looker,inapp_purchase events have a ValidationStatus parameter that can be:

  • Success: The receipt is valid.
  • Failure: The receipt is invalid. Probably a cheater.
  • Error: Something went wrong when trying to validate the receipt, so we can’t define if the receipt is valid.

TroubleShooting

  • Many (or all) transactions showing Error validating IAP/Failure in Looker.

    • Explanation: This kind of failure indicates an error communicating with Google, leading to a Failure status and a validation message, Error validating IAP.
    • Cause: All games published in Lion Studios Google Play account(s) should already have the correct API Service Account credentials in place for all games, so while this is the most common cause for the error, it is not common for our games.

    The cause we see most often is a bug in Google Play that occurs when your IAP bundles are created before linking your API Service Account on Google Play Console. Google assumes you first linked your API Service account, then added your various IAP packages. If you did this out of order, Google won’t recognize your existing IAP bundles.

    • Resolution: Go to your IAP bundle in your Google Play Console and edit some details such as description of the IAP and save it again. We usually add a space, save changes, then remove the added spaceand save again. Any change made to the IAP bundle will trigger Google to reread IAP bundles, and should fix the bug so these bogus failures go away.

FAQ

  • I’m seeing the error: Library\PackageCache\com.lionstudios.beta.iap@0.1.0-b9\Runtime\IAPValidation.cs(128,25): error CS0103: The name ‘STORE NAME’ does not exist in the current context
    • Please switch Unity Build Settings to iOS or Android platform.

Integration of Voxel Buster’s Essential Kit

If you are using this package for your IAPs, follow the steps below to integrate with our IAP Validation module:

  • There is a PurchaseData class which provides information about Receipt.

    • The format of the Voxel asset’s purchaseData is:

      {
          "receipt": {
              "receipt_type": "ProductionSandbox",
              "adam_id": 0,
              "app_item_id": 0,
              "bundle_id": "com.testapp.test",
              "application_version": "1",
              "download_id": 0,
              "version_external_identifier": 0,
              "receipt_creation_date": "2023-11-27 16:06:25 Etc/GMT",
              "receipt_creation_date_ms": "1701101185000",
              "receipt_creation_date_pst": "2023-11-27 08:06:25 America/Los_Angeles",
              "request_date": "2023-11-27 16:22:00 Etc/GMT",
              "request_date_ms": "1701102120026",
              "request_date_pst": "2023-11-27 08:22:00 America/Los_Angeles",
              "original_purchase_date": "2013-08-01 07:00:00 Etc/GMT",
              "original_purchase_date_ms": "1375340400000",
              "original_purchase_date_pst": "2013-08-01 00:00:00 America/Los_Angeles",
              "original_application_version": "1.0",
              "in_app": [
                  {
                      "quantity": "1",
                      "product_id": "com.testapp.test.noads",
                      "transaction_id": "2000000280695735",
                      "original_transaction_id": "2000000280695735",
                      "purchase_date": "2023-02-18 20:32:47 Etc/GMT",
                      "purchase_date_ms": "1676752367000",
                      "purchase_date_pst": "2023-02-18 12:32:47 America/Los_Angeles",
                      "original_purchase_date": "2023-02-18 20:32:47 Etc/GMT",
                      "original_purchase_date_ms": "1676752367000",
                      "original_purchase_date_pst": "2023-02-18 12:32:47 America/Los_Angeles",
                      "is_trial_period": "false",
                      "in_app_ownership_type": "PURCHASED"
                  }
              ]
          },
          "environment": "Sandbox",
          "latest_receipt_info": [
              {
                  "quantity": "1",
                  "product_id": "com.testapp.test.noads",
                  "transaction_id": "2000000280695735",
                  "original_transaction_id": "2000000280695735",
                  "purchase_date": "2023-02-18 20:32:47 Etc/GMT",
                  "purchase_date_ms": "1676752367000",
                  "purchase_date_pst": "2023-02-18 12:32:47 America/Los_Angeles",
                  "original_purchase_date": "2023-02-18 20:32:47 Etc/GMT",
                  "original_purchase_date_ms": "1676752367000",
                  "original_purchase_date_pst": "2023-02-18 12:32:47 America/Los_Angeles",
                  "is_trial_period": "false",
                  "in_app_ownership_type": "PURCHASED"
              }
          ],
          "latest_receipt": "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",
          "status": 0
      }
      
    • The expected format in our validation package is:

      {
        "Payload": "",
        "Store": "AppleAppStore",
        "TransactionID": "transaction-id"
      }
      
  • To resolve the mismatch, use the method below:

    public string ReceiptAsUnityIAPJson()
    {
    	var jsonObject = new
      {
    	  Payload = Transaction.Receipt,
        Store = HardwareUtils.IsIosBuild() ? "AppleAppStore" : "GooglePlay",
        TransactionID = Transaction.Id
      };
    
      return JsonConvert.SerializeObject(jsonObject);
    }