Skip to main content

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.

RV-UnityIAP000.png

  • 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.

  • Open Services > In-App Purchasing > Receipt Validation Obfuscator.
  • If you publish to Google Play, copy-paste your RSA public key. You can find in the Google Play Console under Your App > Monetise > Monetisation setup. Even if you only publish to the Apple App Store, click the button.

RV-UnityIAP000.png


  • 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:

FileDescription
Plugins/SimpleJSONExtension for converting JSON objects to strings and vice-versa.
Scenes/DemoExample scene with demo setup.
Scripts/IAPManagerDemoUnity IAP demo implementation making use of the ReceiptValidator.
Scripts/UIDemoDemo user interface implementation for the sample scene.
Scripts/ReceiptValidatorReceipt Validator implementation sending receipts for validation.
Prefabs/ReceiptValidatorA game object prefab with the ReceiptValidator component attached.
READMEContains links to this documentation and a support notice.

Open the Demo scene.

RV-UnityIAP010.png

  1. In-App Purchases. Initiate IAPs using the buttons, with text below them, stating whether the product has been bought or not.
  2. Users. Select the currently active user by iterating over pre-defined user IDs (Change User button) and fetch its inventory (Get Inventory button).
  3. Try to re-validate all local receipts again, using the current user. This is a so called 'Restore Transactions' action.
  4. Scrollable debug message section.

Select the IAPManager game object in the Hierarchy:

RV-UnityIAP020.png

FieldDescription
Product IDsEnter 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:

RV-UnityIAP025.png

FieldDescription
General DataEnter your App ID here. Also enter your custom user ID, or keep user0 when testing. Note that when making use of user inventory, you have to assign a unique ID per user.
InventoryIf, 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, getting user inventory will work, however validating (non-existent) receipts will fail with the Unsupported Store error message.

RV-UnityIAP030.png

When running on a real device you will be able to fully test all validation workflows. In the following screenshot, a non-consumable and consumable product has been bought. Additionally, the user inventory returns the non-consumable product transaction.

RV-UnityIAP040.png

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:

  1. Instantiate the ReceiptValidator prefab
  2. Initialize the ReceiptValidator component (Initialize)
  3. Extend the ProcessPurchase method with validation (RequestPurchase)
  4. Link the ReceiptValidator's purchaseCallback Action
  5. (optional) Add User Inventory (RequestInventory, GetInventory)
  6. Add a way to let users restore their transactions (RequestRestore)

  1. 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.

  2. 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);
  3. 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 return PurchaseProcessingResult.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 is Purchased (not in case it is Failed) and return PurchaseProcessingResult.Complete.

  4. 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 the ProcessPurchase implementation. This means that both interfaces call the same method to reward the user.

  5. 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 than Disabled and add the following line at the end of OnInitialized:

    ReceiptValidator.Instance.RequestInventory();

    You can either retrieve the inventory by subscribing to the ReceiptValidator.inventoryCallback action or by calling ReceiptValidator.Instance.GetInventory() manually later.

  6. 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, call RequestRestore 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.