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.
💡 |
By default, we do not block cheaters from cheating - they can still get their “stolen” goods; however, we do prevent event data from being sent to networks and flag those transactions as cheaters so that we filter out bad data. However, you can optionally block bad purchases; see the examples below. |
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
💡 |
This setup must be completed correctly for the Validation package to work properly. |
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,
- Google Play Setup - Service Account’s Client Email & Private Key
- AppleStore Setup - Shared Secret
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_failed
,purchase_unknown
andpurchase_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 theUnityEngine.Purchasing.Product
object that Unity Purchasing provides in theProcessPurchase
function’sPurchaseEventArgs
parameter’spurchasedProduct
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
💡
See LionAnalytics documentation for further descriptions of these fields. -
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. TheValidationStatus
will provide the reason for the failure to validate.
💡 |
If you are not using Unity’s Purchasing package, you can replace the Product product parameter with IAPInfo iapInfo .
This object must be constructed with the following elements: transactionID , productID , receiptPayload , isoCurrencyCode , localizedPrice , and localizedTitle .
|
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 aspace
, save changes, then remove the addedspace
and 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.
- Explanation: This kind of failure indicates an error communicating with Google, leading to a
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); }