Unity
Introduction
This is a sample project for the Unity game engine, which demonstrates using the Receipt Validator API in regards to purchase validation and user inventory. It comes with few additions that facilitates testing different User Behaviour modes and trying to restore transactions. Please browse this documentation and also the other chapters for general instructions and explanations on all available Receipt Validator features.
Requirements:
- You have created a free account for the Receipt Validator service
- You have created a new app in the Receipt Validator dashboard
- You should have set up your app and products on the App Store(s) already prior to running this project
Download
Via the Unity Asset Store and Package Manager.
Project Setup
In order to handle billing, this project requires Unity IAP. Currently, only Unity IAP is supported.
- In your project, import the latest Unity IAP package and Unity UI via
Window > Package Manager
.
- Afterwards, import the Receipt Validator asset via the Package Manager. You will receive compilation errors if you import the asset without Unity IAP already present.
Note: this asset uses local and server-side validation. Only if the local validation passes, a server-side validation request will be created. Create your secret files for local validation in the following step.
- Configure Unity IAP's local validation under
Project Settings > Services > In-App Purchasing
. If this section is turned off, enable it first.
Scroll down to locate the Receipt Obfuscator section. The Google Play Public Key can be found in your Google Play Developer Console underYour App > Monetise > Monetisation setup > Google Play Billing > Base64-encoded RSA public key
.
Click the Obfuscate button to generate your Google Play and Apple Tangle files
You can now scroll back up and turn off the In App Purchases
service again! This does not affect purchasing in any way, it just prevents sending analytics to Unity in case you do not intend to use that.
- Verify that your project's Package Name matches your App Store's bundle identifier in
Edit > Project Settings > Player > Other Settings > Identification > Package Name
.
Asset Structure
The package consists of the following files:
File | Description |
---|---|
Plugins/SimpleJSON | Extension for converting JSON objects to strings and vice-versa. |
Scenes/Demo | Example scene with demo setup. |
Scripts/IAPManagerDemo | Unity IAP demo implementation making use of the ReceiptValidator. |
Scripts/UIDemo | Demo user interface implementation for the sample scene. |
Scripts/ReceiptValidator | Receipt Validator implementation sending receipts for validation. |
Prefabs/ReceiptValidator | A game object prefab with the ReceiptValidator component attached. |
README | Contains links to this documentation and a support notice. |
Open the Demo scene. Each product type has a separate purchase button for testing purposes.
Select the IAPManager game object in the Hierarchy:
Field | Description |
---|---|
Product IDs | Enter or customize your Product ID for each supported Product Type, or skip if you do not offer them. They are used for initializing Unity IAP. |
Select the ReceiptValidator game object in the Hierarchy:
Field | Description |
---|---|
General Data | Enter your App ID here. Leave the user ID empty for testing. Note that when making use of user inventory, you have to assign a unique ID per user. |
Inventory | If, how and when to request user inventory. As noted in the Inspector, leave it on Disabled on the FREE plan, without user authentication or when storing user purchases on your own server.Inventory Request Type: Disabled : Do not request user inventory.Once : User inventory can only be requested once per app launch.Delay : User inventory can only be requested once in a certain timespan, by default every 30 minutes. |
Testing
Since the Unity Editor does not have any connection to App Stores, you are not able to test purchases using it. In the Unity Editor, a message is shown mentioning this too. You have to test on a real device that is actually able to receive valid receipts and transactions from the App Store.
If running the scene in the Unity editor, user inventory will not be fetched and trying to validate (non-existent) receipts will succeed immediately without any actual validation.
When running on a real device you will be able to fully test all validation workflows. In the following screenshot, a consumable and non-consumable product has been bought. In a subsequent launch, the user inventory returns the non-consumable product transaction.
Integration
This section explains the asset's core methods and how to integrate them in your own project. For an integration with your own Unity IAP handler, the following points need to be done:
- Instantiate the ReceiptValidator prefab
- Initialize the ReceiptValidator component (
Initialize
) - Extend the ProcessPurchase method with validation (
RequestPurchase
) - Link the ReceiptValidator's purchaseCallback Action
- (optional) Add User Inventory (
RequestInventory
,GetInventory
) - Add a way to let users restore their transactions (
RequestRestore
)
Place the
Prefabs > ReceiptValidator
prefab into the first scene of your application. It calls DontDestroyOnLoad on itself and therefore persists across scene changes, but is not initialized until you do so.In your Unity IAP handler's code:
- add the asset's namespace at the top:
using FLOBUK.ReceiptValidator;
- when you call
ConfigurationBuilder.Instance
to create a store module, save a reference to the ConfigurationBuilder in a variable as we will need it in the next step. In the IAPManagerDemo script, this is done in Start(). - in your
OnInitialized
implementation, initialize the ReceiptValidator component by passing in the IStoreController and ConfigurationBuilder reference stored previously:ReceiptValidator.Instance.Initialize(controller, builder);
- add the asset's namespace at the top:
In your
ProcessPurchase
implementation, pass the received product for validation to the ReceiptValidator and get its PurchaseState:PurchaseState state = ReceiptValidator.Instance.RequestPurchase(product);
If the PurchaseState is
Pending
, it is important that you returnPurchaseProcessingResult.Pending
to keep the transaction open. If the transaction was processed, we will receive a callback from the ReceiptValidator in the next step. Otherwise, the transaction was completed. You will then want to reward the user in case the PurchaseState isPurchased
(not in case it isFailed
) and returnPurchaseProcessingResult.Complete
.In the previous step, we already rewarded the user if the transaction was complete. However, we only handled the Unity IAP part, in case the Receipt Validator was not used. Now, we have to implement the Receipt Validator callback when finishing a transaction too. In
OnInitialized
, below the initialization call add:ReceiptValidator.purchaseCallback += OnPurchaseResult;
To keep things simple, in the IAPManagerDemo script we defined a separate
OnPurchaseResult
method that is also used in theProcessPurchase
implementation. This means that both interfaces call the same method to reward the user.If you are on a paid plan and make use of user authentication and inventory, the user's purchases are stored in the backend. To retrieve them, on the ReceiptValidator prefab set the
Inventory Request Type
to a value other thanDisabled
and add the following line at the end ofOnInitialized
:ReceiptValidator.Instance.RequestInventory();
You can either retrieve the inventory by subscribing to the
ReceiptValidator.inventoryCallback
action or by callingReceiptValidator.Instance.GetInventory()
manually later.When using user inventory, you will want to allow users to manually restore their purchases and re-sync them with the Receipt Validator backend, in case they switch devices, user IDs or lost their local storage in other ways. Add a new method that when run on IOS, calls Unity IAP's native
RestoreTransactions
method which will automatically be processed by the Receipt Validator afterwards. On Android, callRequestRestore
which sends all user receipts to the Receipt Validator again.public void RestoreTransactions()
{
#if UNITY_IOS
extensions.GetExtension<IAppleExtensions>().RestoreTransactions(null);
#else
ReceiptValidator.Instance.RequestRestore();
#endif
}This completes the integration instructions. If you would like to learn more about how user inventory is handled, please see the Advanced section below.
Advanced
In the REST API on the GetUser call, it is noted that requests should not be executed too frequently and limited to a reasonable amount. At best, user inventory should be accessed from memory throughout the session, or stored locally for a period of time. While local storage is out of scope for this asset, we support making use of the memory-approach and offer a delay timer.
For the in-memory inventory, the ReceiptValidator component implements below Dictionary. When calling RequestInventory
, it is filled with the user's inventory response and cached for this session. Upon a new purchase, that purchase is added or replaced in the inventory as well, making another RequestInventory call unnecessary. Whether a product is purchased, is then simply checked on the Dictionary or by calling ReceiptValidator.Instance.IsPurchased
.
Dictionary<string, PurchaseResponse> inventory = new Dictionary<string, PurchaseResponse>();
Regarding limited GetUser call frequency and as a security measure, the Inventory Request Type
= Delay
variable is set to 1800
seconds (30 minutes). This means the client allows one GetUser call every 30 minutes. Note that this is just an example - as described above, you would actually need only one request per app launch (at most) and set the Inventory Request Type
= Once
instead.
For testing and if you would like to quickly verify changes in the user's inventory, you can just restart the app or change the delay in code (inventoryDelay
variable). Do not forget to revert it later!
Additionally, in order to prevent making RequestInventory calls for users without any purchases (and therefore no user inventory that could be retrieved by doing the request), local receipts are first checked for their existence. If there was a local receipt before and the user inventory response is not empty, inventory requests continue for one month. In case of expired subscriptions, this ensures that you are able to remind users to resubscribe or display a message about billing issues inside the app.