View as Markdown

Virtual Store

Listing store items #

You can list available store items.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
foreach (var storeItem in economySystem.StoreItems)
{
    Debug.Log($"{storeItem.Name} - {storeItem.Description}. Cost: ");

    if (!string.IsNullOrEmpty(storeItem.Cost.Sku))
    {
        Debug.Log($"{storeItem.Cost.Sku}");
    }
    else
    {
        foreach (var currencyKvp in storeItem.Cost.Currencies)
        {
            Debug.Log($"{currencyKvp.Key}: {currencyKvp.Value}");
        }
    }
}

Refreshing the store #

You can refresh the economy system’s store data.

1
await economySystem.RefreshStoreAsync();

Purchase Intent #

It’s not necessary to call purchase intent as it’s handled internally by UnityPurchasingSystem.

Getting the active store type #

You can get the active store type.

1
Debug.Log($"Active store type is {economySystem.ActiveStoreType}");

Filtering store items by category #

Filter the cached store items to a specific category using GetStoreItemsByCategory.

1
2
3
4
5
var items = economySystem.GetStoreItemsByCategory("coin_packs");
foreach (var item in items)
{
    Debug.Log($"{item.Name} - {item.Description}");
}

Passing an empty or null category returns all store items.

Purchasing store items #

Soft currency #

For items with no sku, the server deducts the required currencies from the player’s balance and grants the reward.

1
IEconomyPurchaseAck result = await economySystem.PurchaseStoreItemAsync("<itemId>");

In-app purchase #

For hard currency items, use UnityPurchasingSystem. It wraps Unity’s purchasing package and handles the platform purchase flow, receipt submission to Nakama, and reward delivery sequencing for you.

Initialize it in your system list after EconomySystem, passing the same EconomySystem instance as a dependency. See Base for the full initialization code.

1
2
var purchasingSystem = new UnityPurchasingSystem(logger, economySystem);
systems.Add(purchasingSystem);

On initialization, UnityPurchasingSystem reads all store items that have a sku configured from the economy system and registers them with Unity Purchasing automatically. You don’t need to manage Unity Purchasing product registration separately.

1
2
3
PurchaseEventArgs result = await _purchasingSystem.BuyProductAsync(storeItem);
// or by store item ID
PurchaseEventArgs result = await _purchasingSystem.BuyProductByIdAsync("<itemId>");

Adding products manually #

You can manually add products which are not defined in the Economy system. These product definitions could come from the Unity IAP definitions. Products added this way cannot be used with BuyProductAsync or BuyProductByIdAsync to initiate a purchase. Both methods require items to be present in EconomySystem.StoreItems.

1
2
3
4
5
6
var productDefinitions = new List<ProductDefinition>
{
    new ProductDefinition("<productId>", ProductType.Consumable)
};

await purchasingSystem.AddProductsAsync(productDefinitions);
See How to implement IAPs in Unity for a complete guide.

Handle both soft and hard currency purchase flows #

The purchase flow depends on whether the store item is configured for game currency purchase (no sku) or in-app purchase (has sku). See Virtual Store purchases for details on how SKU configuration affects purchases.

This example shows how to handle both purchase types based on the store item’s configuration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public async Task BuyStoreItem(IEconomyListStoreItem storeItem)
{
    if (string.IsNullOrEmpty(storeItem.Cost.Sku))
    {
        // Soft currency purchase - server deducts currency and grants reward
        IEconomyPurchaseAck result = await _economySystem.PurchaseStoreItemAsync(storeItem.Id);

        // Show reward animation UI.
        ShowRewardAnimationUI(result.Reward); // Your custom function.
    }
    else
    {
        // IAP purchase - UnityPurchasingSystem handles the platform flow and Nakama validation
        try
        {
            PurchaseEventArgs result = await _unityPurchasingSystem.BuyProductByIdAsync(storeItem.Id);

            // Reward has already been granted by Nakama. Receipt is available for analytics.
            AnalyticsSendReceipt(result.purchasedProduct.receipt); // Your custom function.

            // Show reward animation UI.
            ShowRewardAnimationUI(storeItem.Reward); // Your custom function.
        }
        catch (PurchaseFailureException e)
        {
            switch (e.Reason)
            {
                case UnityEngine.Purchasing.PurchaseFailureReason.UserCancelled:
                    // Popup some UI.
                    break;
                case UnityEngine.Purchasing.PurchaseFailureReason.PaymentDeclined:
                    // Popup some UI.
                    break;
                case UnityEngine.Purchasing.PurchaseFailureReason.ExistingPurchasePending:
                    // Popup some UI.
                    break;
                case UnityEngine.Purchasing.PurchaseFailureReason.PurchasingUnavailable:
                case UnityEngine.Purchasing.PurchaseFailureReason.ProductUnavailable:
                case UnityEngine.Purchasing.PurchaseFailureReason.SignatureInvalid:
                case UnityEngine.Purchasing.PurchaseFailureReason.DuplicateTransaction:
                case UnityEngine.Purchasing.PurchaseFailureReason.Unknown:
                    break;
            }
        }
    }
}
Soft currency purchases will fail if the store item has a sku but you haven’t configured IAP validation on your server. Remove the sku from items that should only be purchasable with soft currency.