public RemoteConfigManager(IResourceLoader loader, IStorage storage, Uniject.ILogger logger, RuntimePlatform platform, List <ProductDefinition> runtimeProducts = null)
 {
     this.storage  = storage;
     logger.prefix = "Unibill.RemoteConfigManager";
     this.XML      = loader.openTextFile("unibillInventory.json").ReadToEnd();
     Config        = new UnibillConfiguration(XML, platform, logger, runtimeProducts);
     if (Config.UseHostedConfig)
     {
         string val = storage.GetString(CACHED_CONFIG_PATH, string.Empty);
         if (string.IsNullOrEmpty(val))
         {
             logger.Log("No cached config available. Using bundled");
         }
         else
         {
             logger.Log("Cached config found, attempting to parse");
             try {
                 Config = new UnibillConfiguration(val, platform, logger, runtimeProducts);
                 if (Config.inventory.Count == 0)
                 {
                     logger.LogError("No purchasable items in cached config, ignoring.");
                     Config = new UnibillConfiguration(XML, platform, logger, runtimeProducts);
                 }
                 else
                 {
                     logger.Log(string.Format("Using cached config with {0} purchasable items", Config.inventory.Count));
                     XML = val;
                 }
             } catch (Exception e) {
                 logger.LogError("Error parsing inventory: {0}", e.Message);
                 Config = new UnibillConfiguration(XML, platform, logger, runtimeProducts);
             }
         }
         refreshCachedConfig(Config.HostedConfigUrl, logger);
     }
     else
     {
         logger.Log("Not using cached inventory, using bundled.");
         Config = new UnibillConfiguration(XML, platform, logger, runtimeProducts);
     }
 }
        public void purchase(PurchasableItem item, string developerPayload = "")
        {
            if (State == BillerState.INITIALISING)
            {
                logError(UnibillError.BILLER_NOT_READY);
                onPurchaseFailedEvent(item, PurchaseFailureReason.BILLER_NOT_READY);
                return;
            }
            else if (State == BillerState.INITIALISED_WITH_CRITICAL_ERROR)
            {
                logError(UnibillError.UNIBILL_INITIALISE_FAILED_WITH_CRITICAL_ERROR);
                onPurchaseFailedEvent(item, PurchaseFailureReason.BILLING_UNAVAILABLE);
                return;
            }

            if (null == item)
            {
                logger.LogError("Trying to purchase null PurchasableItem");
                return;
            }

            if (!item.AvailableToPurchase)
            {
                logError(UnibillError.UNIBILL_MISSING_PRODUCT, item.Id);
                return;
            }

            if (item.PurchaseType == PurchaseType.NonConsumable && transactionDatabase.getPurchaseHistory(item) > 0)
            {
                logError(UnibillError.UNIBILL_ATTEMPTING_TO_PURCHASE_ALREADY_OWNED_NON_CONSUMABLE);
                onPurchaseFailedEvent(item, PurchaseFailureReason.CANNOT_REPURCHASE_NON_CONSUMABLE);
                return;
            }

            billingSubsystem.purchase(remapper.mapItemIdToPlatformSpecificId(item), developerPayload);
            logger.Log("purchase({0})", item.Id);
        }