/// <summary> /// Grant non-consumables on the user's inventory on PlayFab. Consumables will be granted only /// locally to minimize requests - please call SetFunds or SetPlayerData afterwards if needed. /// </summary> public static IEnumerator SetPurchase(List <string> productIDs) { //create a separate list for non-consumables to request List <string> cloudProducts = new List <string>(); for (int i = 0; i < productIDs.Count; i++) { if ((int)IAPManager.GetIAPObject(productIDs[i]).type > 0) { cloudProducts.Add(productIDs[i]); } } bool commit = true; if (cloudProducts.Count > 0) { ExecuteCloudScriptRequest cloudRequest = new ExecuteCloudScriptRequest() { FunctionName = "grantItems", FunctionParameter = new { itemIds = cloudProducts.ToArray() } }; bool result = false; PlayFabClientAPI.ExecuteCloudScript(cloudRequest, (cloudResult) => { result = true; }, (error) => { OnPlayFabError(error); commit = false; result = true; }); while (!result) { yield return(null); } } //only grant products if the cloud request was successful if (commit == true) { for (int i = 0; i < productIDs.Count; i++) { if (DBManager.GetPurchase(productIDs[i]) == 0) { IAPManager.GetInstance().PurchaseVerified(productIDs[i]); } } } yield return(null); }
/// <summary> /// Sets a product to purchased after successful verification (or without). /// This alters the database entry for non-consumable or products with usage as well. /// </summary> public void PurchaseVerified(string id) { if (!IAPObjects.ContainsKey(id)) { id = GetIAPIdentifier(id); } if (!IAPObjects.ContainsKey(id)) { return; } IAPObject obj = IAPObjects[id]; switch (obj.editorType) { case IAPType.Currency: foreach (IAPCurrency cur in obj.virtualPrice) { DBManager.IncreaseFunds(cur.name, cur.amount); } break; default: //for consumables, add the defined usage count to tracking in player data //on non-consumables, don't continue if the product is already purchased, //for example if we just want to verify an existing product again switch (obj.type) { case ProductType.Consumable: if (obj.usageCount > 0) { DBManager.IncreasePlayerData(id, obj.usageCount); //update server player data #if PLAYFAB && !PLAYFAB_VALIDATION PlayfabManager.SetPlayerData(); #endif } break; default: if (DBManager.GetPurchase(id) > 0) { return; } DBManager.IncreasePurchase(id, 1); break; } break; } purchaseSucceededEvent(id); }
/// <summary> /// Refreshes the visual representation of a specific shop item. /// This is called automatically because of subscribing to the DBManager update event. /// It also means saving performance due to not refreshing all items every time. /// </summary> public void Refresh(string id) { //this method is based on data from the database, //so if we don't have a DBManager instance don't continue if (!DBManager.GetInstance()) { return; } IAPObject obj = IAPManager.GetIAPObject(id); IAPItem item = instance.IAPItems.ContainsKey(id) ? instance.IAPItems[id] : null; if (obj == null || item == null || item.productId != id) { return; } bool isSelected = DBManager.GetSelected(id); bool isPurchased = DBManager.GetPurchase(id) > 0; //double check that selected items are actually owned //if not, correct the entry by setting it to deselected if (isSelected && !isPurchased) { DBManager.SetDeselected(id); isSelected = false; } if (isPurchased) { item.Purchased(true); //in case the item has been selected before, but also auto-select one item per group //more items per group can be pre-selected manually e.g. on app launch if (isSelected || (item.selectButton && !item.deselectButton && DBManager.GetSelectedGroup(IAPManager.GetIAPObjectGroupName(id)).Count == 0)) { item.IsSelected(true); } } else if (!string.IsNullOrEmpty(obj.req.entry) && DBManager.isRequirementMet(obj.req)) { //check if a requirement is set up for this item, //then unlock if the requirement has been met if (IAPManager.isDebug) { Debug.Log("requirement met for: " + obj.id); } item.Unlock(); } }
/// <summary> /// Returns the next unpurchased upgrade id of a product. /// </summary> public static string GetNextUpgrade(string productId) { string id = GetCurrentUpgrade(productId); IAPObject obj = GetIAPObject(id); if (DBManager.GetPurchase(id) == 0 || obj == null || string.IsNullOrEmpty(obj.req.nextId)) { return(id); } else { return(obj.req.nextId); } }
/// <summary> /// Overload for purchasing virtual product based on its product identifier. /// </summary> public static void PurchaseProduct(IAPObject obj) { string productId = obj.id; //product is set to already owned, this should not happen if (obj.type != ProductType.Consumable && DBManager.GetPurchase(productId) > 0) { OnPurchaseFailed("Product already owned."); return; } #if PLAYFAB && !PLAYFAB_VALIDATION new PlayfabStore().Purchase(obj); return; #endif //check whether the player has enough funds bool didSucceed = DBManager.VerifyVirtualPurchase(obj); if (isDebug) { Debug.Log("Purchasing virtual product " + productId + ", result: " + didSucceed); } //on success, non-consumables are saved to the database. This automatically //saves the new substracted fund value, then and fire the succeeded event if (didSucceed) { if (obj.type == ProductType.Consumable) { DBManager.IncreasePlayerData(productId, obj.usageCount); purchaseSucceededEvent(productId); } else { DBManager.SetPurchase(obj.id); } purchaseSucceededEvent(productId); } else { OnPurchaseFailed("Insufficient funds."); } }
/// <summary> /// Returns the last purchased upgrade id of a product, /// or the main product itself if it hasn't been purchased yet. /// </summary> public static string GetCurrentUpgrade(string productId) { if (DBManager.GetPurchase(productId) == 0) { return(productId); } string id = productId; List <string> upgrades = GetIAPUpgrades(productId); for (int i = upgrades.Count - 1; i >= 0; i--) { if (DBManager.GetPurchase(upgrades[i]) > 0) { id = upgrades[i]; break; } } return(id); }
//returning products retrieved by the billing system back to Unity IAP private void OnProductsRetrieved(BillingProduct[] list, string error) { if (!string.IsNullOrEmpty(error)) { OnSetupFailed(error); return; } string itemId = null; for (int i = 0; i < list.Length; i++) { itemId = list[i].ProductIdentifier; if (!products.ContainsKey(itemId)) { products.Add(itemId, new ProductDescription(itemId, new ProductMetadata(list[i].LocalizedPrice, list[i].Name, list[i].Description, list[i].CurrencyCode, (decimal)list[i].Price))); } if (PlayerPrefs.HasKey(itemId)) { products[itemId] = new ProductDescription(itemId, products[itemId].metadata, DBManager.GetReceipt(itemId), ""); } //auto restore products in case database does not match #if UNITY_ANDROID string globalId = IAPManager.GetIAPIdentifier(itemId); if (NPBinding.Billing.IsProductPurchased(list[i]) && DBManager.GetPurchase(globalId) == 0) { DBManager.SetPurchase(globalId); } #endif } callback.OnProductsRetrieved(products.Values.ToList()); }