Example #1
0
        private async Task PurchaseSubscription(string subCode)
        {
            if (_userCache.GetLoggedInAccount() == null)
            {
                throw new InvalidOperationException("User must be logged in to purchase a subscription.");
            }
            var sub = await _purchaseService.PurchaseItem(subCode, ItemType.Subscription, "payload");

            if (sub == null)
            {
                throw new InvalidOperationException($"Something went wrong when attempting to purchase. Please try again.");
            }

            var model = new Models.SubscriptionModel()
            {
                PurchaseId        = sub.Id,
                PurchaseToken     = sub.PurchaseToken,
                SubscriptionType  = SubscriptionUtility.GetTypeFromProductId(sub.ProductId),
                StartDateTime     = DateTimeOffset.UtcNow,
                PurchasedDateTime = DateTimeOffset.UtcNow,
                EndDateTime       = DateTimeOffset.UtcNow.AddMonths(1),
                PurchaseSource    = Device.RuntimePlatform == Device.Android ? Models.PurchaseSource.GooglePlay : Models.PurchaseSource.AppStore,
                UserId            = _userCache.GetLoggedInAccount().UserId,
                Email             = _userCache.GetLoggedInAccount().Email
            };

            try
            {
                _subCache.Put(model.PurchaseId, model);
            }
            catch { }
            _subService.AddSubscription(model);
        }
        private string GetLegalJargon(ValidationModel validation)
        {
            string platformText = _runtimePlatform == Device.Android ? "Play Store" : "iTunes";
            var    costDesc     = $"${SubscriptionUtility.IndvReportNoSubPrice.ToString()}";

            if (validation.Subscription != null)
            {
                var info = SubscriptionUtility.GetInfoFromSubType(validation.Subscription.SubscriptionType);
                switch (validation.Subscription.SubscriptionType)
                {
                case SubscriptionType.Premium:
                    costDesc = $"${SubscriptionUtility.IndvReportPremiumPrice.ToString()}";
                    break;

                case SubscriptionType.Enterprise:
                    costDesc = $"${SubscriptionUtility.IndvReportEnterprisePrice.ToString()}";
                    break;

                default:
                    costDesc = $"${SubscriptionUtility.IndvReportBasicPrice.ToString()}";
                    break;
                }
            }
            return($@"A {costDesc} purchase will be applied to your {platformText} account upon confirmation. " +
                   $@"For more information, ");
        }
Example #3
0
        private async Task SetVisualStateForValidation()
        {
            try
            {
                var user = _userService.GetLoggedInAccount();
                if (user == null)
                {
                    MainLayoutVisible         = true;
                    CannotSubmitLayoutVisible = false;
                    return;
                }
                var validation = await _orderValidator.ValidateOrderRequest(user);

                var activeSub = SubscriptionUtility.SubscriptionActive(validation.Subscription);
                if (validation.RemainingOrders > 0)
                {
                    MainLayoutVisible         = true;
                    CannotSubmitLayoutVisible = false;
                    return;
                }
                switch (validation.State)
                {
                case ValidationState.NoReportsLeftInPeriod:
                    MainLayoutVisible         = false;
                    CannotSubmitHeaderText    = "You've been busy!";
                    CannotSubmitLabelText     = $"Sorry, you have used all of your reports for this month.";
                    CannotSubmitLayoutVisible = true;
                    PurchaseOptionsText       = $"Additional reports can be purchased at a reduced price of " +
                                                $"${SubscriptionUtility.GetSingleReportInfo(validation).Price} per report.";
                    PurchaseOptionsVisible = true;
                    break;

                case ValidationState.NoSubscriptionAndTrialValid:
                    MainLayoutVisible         = false;
                    CannotSubmitHeaderText    = "Thanks for trying Fair Squares!";
                    CannotSubmitLabelText     = $"Please claim your free one month subscription trial, or click below to view other options.";
                    CannotSubmitLayoutVisible = true;
                    PurchaseOptionsVisible    = false;
                    break;

                case ValidationState.NoSubscriptionAndTrialAlreadyUsed:
                    MainLayoutVisible         = false;
                    CannotSubmitHeaderText    = "Thanks for trying Fair Squares!";
                    CannotSubmitLabelText     = $"Click below to view options for getting more reports.";
                    CannotSubmitLayoutVisible = true;
                    PurchaseOptionsVisible    = false;
                    break;

                default:
                    MainLayoutVisible         = true;
                    CannotSubmitLayoutVisible = false;
                    break;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to set visual state on load.", ex);
            }
        }
Example #4
0
        public async Task WhenPurchasingSubscription_PurchasesCorrect(SubscriptionType type)
        {
            var svc      = GetService();
            var name     = SubscriptionUtility.GetInfoFromSubType(type).SubscriptionCode;
            var purchase = await svc.PurchaseItem(name, ItemType.Subscription, "");

            _billing.Verify(x => x.ConnectAsync(ItemType.Subscription), Times.Once);
            _billing.Verify(x => x.DisconnectAsync(), Times.Once);
            _billing.Verify(x => x.PurchaseAsync(name, ItemType.Subscription, "", null), Times.Once);
            _logger.Verify(x => x.LogError(It.IsAny <string>(), It.IsAny <Exception>()), Times.Never);
        }
 private void SetViewState(ValidationModel validation)
 {
     PurchaseButtonText    = $"Purchase Report";
     LegalText             = GetLegalJargon(validation);
     ReportsDescVisible    = true;
     CostDescVisible       = true;
     PurchaseButtonEnabled = true;
     MarketingDescVisible  = true;
     MarketingDescText     = $"Purchase one additional report.";
     ReportsDescText       = $"Adds one available report to your account.";
     CostDescText          = $"${SubscriptionUtility.GetSingleReportInfo(validation).Price}";
 }
Example #6
0
        public OrderViewModel(IOrderValidationService validator,
                              ICurrentUserService userCache,
                              IOrderService orderService,
                              IToastService toast,
                              IPageFactory pageFactory,
                              MainThreadNavigator nav,
                              IMessagingSubscriber topicSubscriber,
                              ILogger <OrderViewModel> logger,
                              string deviceType,
                              AlertUtility alertUtility,
                              Action <BaseNavPageType> baseNavigationAction,
                              ICache <Models.Order> orderCache)
        {
            _orderValidator       = validator;
            _userService          = userCache;
            _toast                = toast;
            _nav                  = nav;
            _orderService         = orderService;
            _pageFactory          = pageFactory;
            _orderCache           = orderCache;
            _alertUtility         = alertUtility;
            _topicSubscriber      = topicSubscriber;
            _baseNavigationAction = baseNavigationAction;
            _deviceType           = deviceType;
            _logger               = logger;

            PurchaseOptionsCommand = new Command(async() =>
            {
                var val = await _orderValidator.ValidateOrderRequest(_userService.GetLoggedInAccount());
                if (SubscriptionUtility.SubscriptionActive(val.Subscription))
                {
                    _nav.Push(_pageFactory.GetPage(PageType.SingleReportPurchase, val));
                }
                else
                {
                    _nav.Push(_pageFactory.GetPage(PageType.PurchaseOptions, val));
                }
            });
            OptionsInfoCommand = new Command(async() => await alertUtility.Display("Roof Option Selection",
                                                                                   $"Selecting a roof option allows Fair Squares to determine what roofs you would like measured at the submitted address.{Environment.NewLine}" +
                                                                                   $"{Environment.NewLine}Primary Only- Fair Squares will measure the primary structure, including attached garage.{Environment.NewLine}" +
                                                                                   $"Detached Garage- Fair Squares will also measure the detached garage on the property.{Environment.NewLine}" +
                                                                                   $"Shed/Barn- Fair Squares will also measure a shed or barn on the property.{Environment.NewLine}" +
                                                                                   $"{Environment.NewLine}NOTE: Fair Squares only supports measuring one primary structure per report (with a detached garage or shed/barn if needed).",
                                                                                   "Ok"));
            ErrorMessageRowHeight = 0;
            SelectedOptionIndex   = -1;
            SelectedStateIndex    = -1;
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
            SetVisualStateForValidation();
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
        }
Example #7
0
        public async Task WhenActiveSubscriptionAndAllOrdersUsed_ShouldNotValidate(SubscriptionType type)
        {
            _subCache.Put("1", new SubscriptionModel()
            {
                EndDateTime      = DateTimeOffset.Now.AddDays(2),
                SubscriptionType = type,
                StartDateTime    = DateTimeOffset.Now.AddDays(-20)
            });
            _orderCache.Put(Enumerable.Range(0, SubscriptionUtility.GetInfoFromSubType(type).OrderCount + 1)
                            .ToDictionary(x => x.ToString(), x => new Order()
            {
                DateReceived = DateTimeOffset.Now.AddDays(-2)
            }));

            var valSvc = new OrderValidationService(_refresher.Object, _orderCache, _subCache, _prCache, _logger.Object);
            var result = await valSvc.ValidateOrderRequest(new MobileClient.Authentication.AccountModel()
            {
                UserId = "234"
            });

            Assert.AreEqual(ValidationState.NoReportsLeftInPeriod, result.State);
        }
Example #8
0
        public async Task WhenActiveSubscriptionAndRemainingOrders_ShouldHaveOrdersLeft(SubscriptionType type, int orderCount)
        {
            _subCache.Put("1", new SubscriptionModel()
            {
                EndDateTime      = DateTimeOffset.Now.AddDays(2),
                SubscriptionType = type,
                StartDateTime    = DateTimeOffset.Now.AddDays(-20)
            });
            _orderCache.Put(Enumerable.Range(0, orderCount + 1)
                            .ToDictionary(x => x.ToString(), x => new Order()
            {
                DateReceived = DateTimeOffset.Now.AddDays(-2)
            }));

            var valSvc = new OrderValidationService(_refresher.Object, _orderCache, _subCache, _prCache, _logger.Object);
            var result = await valSvc.ValidateOrderRequest(new AccountModel()
            {
                UserId = "12345"
            });

            Assert.AreEqual(ValidationState.SubscriptionReportValid, result.State);
            Assert.AreEqual(SubscriptionUtility.GetInfoFromSubType(type).OrderCount - orderCount, result.RemainingOrders);
        }
Example #9
0
        private async void PurchaseButtonClicked()
        {
            LoadAnimationRunning  = true;
            LoadAnimationVisible  = true;
            PurchaseButtonEnabled = false;
            try
            {
                SubscriptionType selected = SubscriptionType.Basic;
                var subCode = SubscriptionUtility.GetInfoFromSubType(selected).SubscriptionCode;
                try
                {
                    await PurchaseSubscription(subCode);

                    await _alertUtility.Display("Purchase Complete", "Thank you for your purchase!", "Ok");

                    _nav.PopToRoot();
                }
                catch (Exception ex)
                {
                    _toastService.LongToast($"Failed to purchase subscription. {ex.Message}");
                }
            }
            catch (Exception ex)
            {
                _toastService.LongToast($"Something went wrong when trying to purchase subscription. {ex.Message}");
            }
            finally
            {
                try
                {
                    PurchaseButtonEnabled = true;
                    LoadAnimationRunning  = false;
                    LoadAnimationVisible  = false;
                }
                catch { }
            }
        }
Example #10
0
        private async Task SetSubState(AccountModel user)
        {
            try
            {
                if (user == null)
                {
                    SubscriptionLabel = $"Please log in to view purchasing options.";
                    _changeSubStyleClass("btn-success");
                    SubscriptionButtonText    = "View Options";
                    SubscriptionButtonEnabled = false;
                    return;
                }
                if (_cacheRefresher.Invalidated)
                {
                    await(_cacheRefresher.RefreshTask ?? Task.CompletedTask);
                }
                var validity = await _orderValidator.ValidateOrderRequest(user);

                var activeSub = SubscriptionUtility.SubscriptionActive(validity.Subscription);
                _changeSubStyleClass(activeSub ? "btn-primary" : "btn-success");
                SubscriptionButtonText    = activeSub ? "Manage" : "View Options";
                SubscriptionButtonEnabled = true;
                if (validity.RemainingOrders > 0)
                {
                    SubscriptionLabel = $"Reports remaining: {validity.RemainingOrders.ToString()}";
                }
                else
                {
                    SubscriptionLabel = $"No reports remaining. " +
                                        $"{(activeSub ? "Additional reports can be purchased at a reduced price. Click below to find out more." : "Click below to view purchase options.")}";
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Failed to set subscription state.", ex);
            }
        }
        private async Task PurchaseButtonClicked()
        {
            LoadAnimationRunning  = true;
            LoadAnimationVisible  = true;
            PurchaseButtonEnabled = false;
            try
            {
                var code = SubscriptionUtility.GetSingleReportInfo(_validation).Code;
                try
                {
                    await PurchaseItem(code);

                    await _alertUtility.Display("Purchase Complete", "Thank you for your purchase!", "Ok");

                    _nav.PopToRoot();
                }
                catch (Exception ex)
                {
                    _toastService.LongToast($"Failed to purchase report. {ex.Message}");
                }
            }
            catch (Exception ex)
            {
                _toastService.LongToast($"Something went wrong when trying to purchase report. {ex.Message}");
            }
            finally
            {
                try
                {
                    PurchaseButtonEnabled = true;
                    LoadAnimationRunning  = false;
                    LoadAnimationVisible  = false;
                }
                catch { }
            }
        }
Example #12
0
        public async Task <ValidationModel> ValidateOrderRequest(AccountModel user, bool useCached = true)
        {
            try
            {
                List <SubscriptionModel>    subs      = null;
                List <PurchasedReportModel> purchased = null;
                IEnumerable <Models.Order>  orders    = null;
                SubscriptionModel           lastSub   = null;
                if (!useCached)
                {
                    await _cacheRefresher.RefreshCaches(user);
                }
                subs      = _subCache.GetAll().Select(x => x.Value).OrderBy(x => x.StartDateTime).ToList();
                lastSub   = subs.LastOrDefault();
                purchased = _prCache.GetAll().Select(x => x.Value).ToList();
                orders    = _orderCache.GetAll().Select(x => x.Value);

                var totalRemainingOrders = subs
                                           .Select(x => SubscriptionUtility.GetInfoFromSubType(x.SubscriptionType).OrderCount).Sum() + 1
                                           + purchased.Count()
                                           - orders.Count();
                var model = new ValidationModel()
                {
                    PurchasedReports = purchased,
                    RemainingOrders  = totalRemainingOrders
                };

                if (!subs.Any() && !orders.Any())
                {
                    model.State   = ValidationState.FreeReportValid;
                    model.Message = "User can use their free report.";
                    return(model);
                }
                // Handle case of no sub but purchased reports
                if (!subs.Any() && purchased.Any())
                {
                    if (orders.Count() < (1 + purchased.Count()))
                    {
                        model.State   = ValidationState.NoSubscriptionAndReportValid;
                        model.Message = "User can use their purchased report.";
                        return(model);
                    }
                }
                if (!subs.Any() && orders.Any())
                {
                    // This case means they've never had a subscription before, and are eligible for a trial month.
                    model.State   = ValidationState.NoSubscriptionAndTrialValid;
                    model.Message = "User has used their free report, but is eligible for a free trial period.";
                    return(model);
                }
                if (subs.Any() && !SubscriptionUtility.SubscriptionActive(lastSub) && orders.Any())
                {
                    model.State   = ValidationState.NoSubscriptionAndTrialAlreadyUsed;
                    model.Message = "User has used their free trial and free report.";
                    return(model);
                }

                if (totalRemainingOrders <= 0)
                {
                    model.State           = ValidationState.NoReportsLeftInPeriod;
                    model.RemainingOrders = 0;
                    model.Subscription    = lastSub;
                    model.Message         = "User has used all of their orders for this subscription period.";
                    return(model);
                }
                else
                {
                    model.State           = ValidationState.SubscriptionReportValid;
                    model.Subscription    = lastSub;
                    model.RemainingOrders = totalRemainingOrders;
                    return(model);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to validate user's order.", ex);
                throw ex;
            }
        }
Example #13
0
 private async Task SetInitialState(AccountModel user)
 {
     ToolbarInfoCommand = new Command(() => _navigation.Push(_pageFactory.GetPage(PageType.Instruction, false)));
     LogOutCommand      = new Command(async() =>
     {
         if (_userCache.GetLoggedInAccount() == null)
         {
             _navigation.Push(_pageFactory.GetPage(PageType.Landing));
         }
         else
         {
             _userCache.LogOut();
             SetAccountState(null);
             await SetSubState(null);
         }
     });
     SubscriptionCommand = new Command(async() =>
     {
         if (_userCache.GetLoggedInAccount() == null)
         {
             return;
         }
         var valid = await _orderValidator.ValidateOrderRequest(_userCache.GetLoggedInAccount());
         try
         {
             SubscriptionButtonEnabled = false;
             if (SubscriptionUtility.SubscriptionActive(valid.Subscription))
             {
                 _navigation.Push(_pageFactory.GetPage(PageType.ManageSubscription, valid));
             }
             else
             {
                 _navigation.Push(_pageFactory.GetPage(PageType.PurchaseOptions, valid));
             }
         }
         catch (Exception ex)
         {
             _logger.LogError("Failed to handle subscription click command.", ex);
         }
         finally
         {
             SubscriptionButtonEnabled = true;
         }
     });
     FeedbackCommand = new Command(() =>
     {
         try
         {
             FeedbackButtonEnabled = false;
             _navigation.Push(_pageFactory.GetPage(PageType.Feedback, _navigation));
         }
         catch (Exception ex)
         {
             _logger.LogError("Failed to handle feedback click command.", ex);
         }
         finally
         {
             FeedbackButtonEnabled = true;
         }
     });
     SetAccountState(user);
     await SetSubState(user);
 }
 public ManageSubscriptionViewModel(ValidationModel model,
                                    string runtimePlatform,
                                    Action <Uri> openUri,
                                    MainThreadNavigator nav,
                                    ILogger <ManageSubscriptionViewModel> logger,
                                    IPageFactory pageFactory)
 {
     _model           = model;
     _runtimePlatform = runtimePlatform;
     _openUri         = openUri;
     _nav             = nav;
     _logger          = logger;
     _pageFactory     = pageFactory;
     try
     {
         SubscriptionTypeLabel = "   " + _model.Subscription.SubscriptionType.ToString();
         RemainingOrdersLabel  = "   " + _model.RemainingOrders.ToString();
         PurchasedOrdersLabel  = "   " + _model.PurchasedReports?.Count.ToString();
         EndDateLabel          = "   " + _model.Subscription.EndDateTime.ToString("dddd, dd MMMM yyyy");
         GetMoreReportsLabel   = $"Purchase additional reports at a reduced price of ${SubscriptionUtility.GetSingleReportInfo(_model).Price} per report.";
         GetMoreReportsCommand = new Command(() => _nav.Push(_pageFactory.GetPage(PageType.SingleReportPurchase, _model)));
         var compName   = _runtimePlatform == Device.Android ? "Google" : "Apple";
         var supportUri = _runtimePlatform == Device.Android ? "https://support.google.com/googleplay/answer/7018481" :
                          "https://support.apple.com/en-us/HT202039#subscriptions";
         DisclaimerLabel  = $"NOTE: {compName} does not allow subscriptions to be cancelled through the app. This button will open a web browser with instructions on how to cancel from your device.";
         CancelSubCommand = new Command(() => _openUri(new Uri(supportUri)));
     }
     catch (Exception ex)
     {
         _logger.LogError($"Failed to load manage subscription page.", ex);
     }
 }
Example #15
0
        public async Task WhenNotAllOrdersUsedFromOneSub_RollsOverToNextMonth(int totalOrders, ValidationState expected)
        {
            var subs = new List <SubscriptionModel>()
            {
                new SubscriptionModel()
                {
                    PurchaseId       = "1",
                    EndDateTime      = DateTimeOffset.Now.AddDays(2),
                    SubscriptionType = SubscriptionType.Basic,
                    StartDateTime    = DateTimeOffset.Now.AddDays(-20)
                },
                new SubscriptionModel()
                {
                    PurchaseId       = "2",
                    EndDateTime      = DateTimeOffset.Now.AddDays(-20),
                    SubscriptionType = SubscriptionType.Premium,
                    StartDateTime    = DateTimeOffset.Now.AddDays(-40)
                },
                new SubscriptionModel()
                {
                    PurchaseId       = "3",
                    EndDateTime      = DateTimeOffset.Now.AddDays(-40),
                    SubscriptionType = SubscriptionType.Enterprise,
                    StartDateTime    = DateTimeOffset.Now.AddDays(-60)
                },
                new SubscriptionModel()
                {
                    PurchaseId       = "4",
                    EndDateTime      = DateTimeOffset.Now.AddDays(-60),
                    SubscriptionType = SubscriptionType.Basic,
                    StartDateTime    = DateTimeOffset.Now.AddDays(-80)
                }
            };
            var prs = new List <PurchasedReportModel>()
            {
                new PurchasedReportModel()
                {
                    PurchaseId = "1"
                },
                new PurchasedReportModel()
                {
                    PurchaseId = "2"
                }
            };
            var purchasedOrderCount = subs.Select(x => SubscriptionUtility.GetInfoFromSubType(x.SubscriptionType).OrderCount).Sum() + prs.Count();

            _subCache.Put(subs.ToDictionary(x => x.PurchaseId, x => x));
            _prCache.Put(prs.ToDictionary(x => x.PurchaseId, x => x));
            _orderCache.Put(Enumerable.Range(0, totalOrders + 1)
                            .ToDictionary(x => x.ToString(), x => new Order()
            {
                Fulfilled    = true,
                DateReceived = DateTimeOffset.Now.AddDays(-2)
            }));
            var valSvc = new OrderValidationService(_refresher.Object, _orderCache, _subCache, _prCache, _logger.Object);
            var result = await valSvc.ValidateOrderRequest(new MobileClient.Authentication.AccountModel()
            {
                UserId = "234"
            });

            var expectedCount = purchasedOrderCount - totalOrders > 0 ? purchasedOrderCount - totalOrders : 0;

            Assert.AreEqual(expectedCount, result.RemainingOrders);
            Assert.AreEqual(expected, result.State);
        }
Example #16
0
        public async Task <InAppBillingPurchase> PurchaseItem(string name, ItemType iapType, string payload)
        {
            if (!SubscriptionUtility.ValidatePurchaseType(name, iapType))
            {
                throw new InvalidOperationException($"The item that is attempting to be purchased does not have the proper IAP type.");
            }
            try
            {
                if (_isPurchasing)
                {
                    throw new InvalidOperationException("A different purchase is already in progress. Please try again.");
                }
                _isPurchasing = true;
                bool connected = false;
                try
                {
                    connected = await _billing.ConnectAsync(iapType);
                }
                catch (Exception ex)
                {
                    var message = $"Error occurred while try to connect to billing service.";
                    _logger.LogError(message, ex, name, payload);
                    throw new InvalidOperationException(message);
                }
                if (!connected)
                {
                    // we are offline or can't connect, don't try to purchase
                    _logger.LogError($"Can't connect to billing service.", null, name, payload);
                    throw new InvalidOperationException($"Can't connect to billing service. Check connection to internet.");
                }

                InAppBillingPurchase purchase = null;
                try
                {
                    //check purchases
                    purchase = await _billing.PurchaseAsync(name, iapType, payload);
                }
                catch (InAppBillingPurchaseException purchaseEx)
                {
                    var errorMsg = "";
                    switch (purchaseEx.PurchaseError)
                    {
                    case PurchaseError.AlreadyOwned:
                        errorMsg = $"Current user already owns this item.";
                        break;

                    case PurchaseError.AppStoreUnavailable:
                        errorMsg = "The App Store is unavailable. Please check your internet connection and try again.";
                        break;

                    case PurchaseError.BillingUnavailable:
                        errorMsg = "The billing service is unavailable. Please check your internet connection and try again.";
                        break;

                    case PurchaseError.DeveloperError:
                        errorMsg = "A configuration error occurred during billing. Please contact Fair Squares support.";
                        break;

                    case PurchaseError.InvalidProduct:
                        errorMsg = "This product was not found. Please contact Fair Squares support.";
                        break;

                    case PurchaseError.PaymentInvalid:
                        errorMsg = "A payment configuration error occurred during billing. Please contact Fair Squares support.";
                        break;

                    case PurchaseError.PaymentNotAllowed:
                        errorMsg = "Current user is not allowed to authorize payments.";
                        break;

                    case PurchaseError.ProductRequestFailed:
                        errorMsg = "The product request failed. Please try again.";
                        break;

                    case PurchaseError.ServiceUnavailable:
                        errorMsg = "The network connection is not available. Please check your internet connection.";
                        break;

                    case PurchaseError.UserCancelled:
                        errorMsg = "The transaction was cancelled by the user.";
                        break;

                    default:
                        errorMsg = "An error occurred while purchasing subscription. Please contact Fair Squares support.";
                        break;
                    }
                    try
                    {
                        if (purchaseEx.PurchaseError != PurchaseError.UserCancelled)
                        {
                            _logger.LogError($"Error type: '{purchaseEx.PurchaseError.ToString()}'. Error Message displayed to user: '******'.",
                                             purchaseEx, name, payload);
                        }
                    }
                    catch { }
                    throw new InvalidOperationException(errorMsg, purchaseEx);
                }
                catch (Exception ex)
                {
                    // Something else has gone wrong, log it
                    _logger.LogError("Failed to purchase subscription.", ex, name);
                    throw new InvalidOperationException(ex.Message, ex);
                }
                finally
                {
                    await _billing.DisconnectAsync();
                }
                if (purchase == null)
                {
                    // did not purchase
                    _logger.LogError($"Failed to purchase {name}.", null, name);
                    throw new InvalidOperationException("Failed to complete purchase. Please contact support.");
                }
                return(purchase);
            }
            catch
            {
                throw;
            }
            finally
            {
                _isPurchasing = false;
            }
        }
        static App()
        {
            try
            {
                Container = new Container();
                var analyticsSvc    = DependencyService.Get <IAnalyticsService>();
                var userService     = new CurrentUserService();
                var orderService    = new AzureOrderService(new HttpClient(), _orderEndpoint, _apiKey);
                var propertyService = new PropertyService(new HttpClient(), _propertyEndpoint, new AnalyticsLogger <PropertyService>(analyticsSvc, userService));
                var imageService    = new BlobImageService(new HttpClient(), _blobEndpoint, new AnalyticsLogger <BlobImageService>(analyticsSvc, userService));
                var subService      = new SubscriptionService(new HttpClient(), _subEndpoint, new AnalyticsLogger <SubscriptionService>(analyticsSvc, userService));
                var prService       = new PurchasedReportService(new HttpClient(), _purchasedReportsEndpoint, new AnalyticsLogger <PurchasedReportService>(analyticsSvc, userService));
                var authenticator   = new OAuth2Authenticator(Configuration.ClientId,
                                                              null,
                                                              Configuration.Scope,
                                                              new Uri(GoogleAuthorizeUrl),
                                                              new Uri(Configuration.RedirectNoPath + ":" + Configuration.RedirectPath),
                                                              new Uri(GoogleAccessTokenUrl),
                                                              null,
                                                              true);
                var notifyService       = new NotificationService(new HttpClient(), _notifyEndpoint, _apiKey);
                var purchaseEmailLogger = new EmailLogger <PurchasingService>(notifyService, userService);
                var purchaseService     = new PurchasingService(CrossInAppBilling.Current, purchaseEmailLogger);
                authenticator.Completed += (s, e) =>
                {
                    if (e.IsAuthenticated)
                    {
                        userService.LogIn(e.Account);
                    }
                };

                // Setup caches and begin process of filling them.
                var dbBasePath    = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
                var propertyCache = new LocalSqlCache <PropertyModel>(Path.Combine(dbBasePath, "property.db3"),
                                                                      new AnalyticsLogger <LocalSqlCache <PropertyModel> >(analyticsSvc, userService));
                var orderCache = new LocalSqlCache <Models.Order>(Path.Combine(dbBasePath, "order.db3"),
                                                                  new AnalyticsLogger <LocalSqlCache <Models.Order> >(analyticsSvc, userService));
                var imageCache = new LocalSqlCache <ImageModel>(Path.Combine(dbBasePath, "images.db3"),
                                                                new AnalyticsLogger <LocalSqlCache <ImageModel> >(analyticsSvc, userService));
                var subCache = new LocalSqlCache <SubscriptionModel>(Path.Combine(dbBasePath, "subs.db3"),
                                                                     new AnalyticsLogger <LocalSqlCache <SubscriptionModel> >(analyticsSvc, userService));
                var settingsCache = new LocalSqlCache <SettingsModel>(Path.Combine(dbBasePath, "sets.db3"),
                                                                      new AnalyticsLogger <LocalSqlCache <SettingsModel> >(analyticsSvc, userService));
                var prCache = new LocalSqlCache <PurchasedReportModel>(Path.Combine(dbBasePath, "purchasedreports.db3"),
                                                                       new AnalyticsLogger <LocalSqlCache <PurchasedReportModel> >(analyticsSvc, userService));

                var    startUpLogger = new AnalyticsLogger <App>(analyticsSvc, userService);
                Action ClearCaches   = () =>
                {
                    try
                    {
                        orderCache.Clear();
                        propertyCache.Clear();
                        imageCache.Clear();
                        subCache.Clear();
                        prCache.Clear();
                    }
                    catch { }
                };
                Func <AccountModel, Task> RefreshCaches = user =>
                {
                    var userId = user.UserId;
                    var prTask = Task.Run(() =>
                    {
                        try
                        {
                            var prs = prService.GetPurchasedReports(userId);
                            prCache.Put(prs.ToDictionary(x => x.PurchaseId, x => x));
                        }
                        catch (Exception ex)
                        {
                            startUpLogger.LogError("Failed to get purchased reports on refresh.", ex);
                        }
                    });
                    var orderTask = Task.Run(async() =>
                    {
                        try
                        {
                            var orders   = await orderService.GetMemberOrders(userId);
                            var unCached = orders.Except(orderCache.GetAll().Select(x => x.Value).ToList(), new OrderEqualityComparer()).ToList();
                            orderCache.Put(orders.ToDictionary(x => x.OrderId, x => x));
                            var subTask = Task.Run(() =>
                            {
                                try
                                {
                                    DependencyService.Get <IMessagingSubscriber>().Subscribe(orders.Select(x => $"{(Device.RuntimePlatform == Device.Android ? App.TopicPrefix : "")}{x.OrderId}").ToList());
                                }
                                catch { }
                            });
                            var propTask = Task.Run(async() =>
                            {
                                if (!orders.Any())
                                {
                                    propertyCache.Clear();
                                    return;
                                }
                                var properties = await propertyService.GetProperties(unCached.Select(x => x.OrderId).ToList());
                                propertyCache.Update(properties);
                            });
                            var imgTask = Task.Run(() =>
                            {
                                if (!orders.Any())
                                {
                                    imageCache.Clear();
                                    return;
                                }
                                var images = imageService.GetImages(unCached.Select(x => x.OrderId).ToList());
                                imageCache.Update(images);
                            });
                            var subscriptionTask = Task.Run(async() =>
                            {
                                try
                                {
                                    // TODO: Refactor this so it can be tested.
                                    var allSubs              = subService.GetSubscriptions(userId).OrderBy(x => x.StartDateTime).ToList();
                                    var recentSub            = allSubs.LastOrDefault();
                                    var purchases            = new List <InAppBillingPurchase>();
                                    SubscriptionModel newSub = null;

                                    // Check app store purchases to see if they auto-renewed
                                    if (recentSub != null && !SubscriptionUtility.SubscriptionActive(recentSub))
                                    {
                                        try
                                        {
                                            purchases = (await purchaseService.GetPurchases(ItemType.Subscription)).ToList();
                                        }
                                        catch (Exception ex)
                                        {
                                            purchaseEmailLogger.LogError($"Error occurred while getting purchases.", ex);
                                        }
                                        var mostRecent = purchases.OrderBy(x => x.TransactionDateUtc)?.LastOrDefault();
                                        if (mostRecent != null)
                                        {
                                            newSub = SubscriptionUtility.GetModelFromIAP(mostRecent, user, recentSub);
                                            if (newSub != null)
                                            {
                                                allSubs.Add(newSub);
                                                subService.AddSubscription(newSub);
                                            }
                                        }
                                    }
                                    if (!allSubs.Any())
                                    {
                                        subCache.Clear();
                                    }
                                    else
                                    {
                                        subCache.Put(allSubs.ToDictionary(x => x.PurchaseId, x => x));
                                    }
                                }
                                catch (Exception ex)
                                {
                                    startUpLogger.LogError("Failed to get subscriptions on refresh.", ex);
                                }
                            });
                            await Task.WhenAll(new[] { propTask, imgTask, subTask, subscriptionTask });
                        }
                        catch (Exception ex)
                        {
                            startUpLogger.LogError($"Failed to fill caches.\n{ex.ToString()}", ex);
                        }
                    });
                    return(Task.WhenAll(new[] { prTask, orderTask }));
                };

                var refresher = new CacheRefresher(new AnalyticsLogger <CacheRefresher>(analyticsSvc, userService), RefreshCaches);
                refresher.Invalidate();
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
                refresher.RefreshCaches(userService.GetLoggedInAccount());
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed

                userService.OnLoggedIn += async(s, e) =>
                {
                    ClearCaches();
                    await refresher.RefreshCaches(e.Account);
                };
                userService.OnLoggedOut += (s, e) => ClearCaches();

                // Register services
                Container.Register <IOrderService>(() => orderService, Lifestyle.Singleton);
                Container.Register <IPropertyService>(() => propertyService, Lifestyle.Singleton);
                Container.Register <IImageService>(() => imageService, Lifestyle.Singleton);
                Container.Register <INotificationService>(() => notifyService, Lifestyle.Singleton);
                Container.Register <ILogger <SingleReportPurchaseViewModel> >(() =>
                                                                              new EmailLogger <SingleReportPurchaseViewModel>(notifyService, userService), Lifestyle.Singleton);
                Container.RegisterConditional(typeof(ILogger <>), typeof(AnalyticsLogger <>), c => !c.Handled);
                Container.Register <OAuth2Authenticator>(() => authenticator, Lifestyle.Singleton);
                Container.Register <AccountStore>(() => AccountStore.Create(), Lifestyle.Singleton);
                Container.Register <ICurrentUserService>(() => userService, Lifestyle.Singleton);
                Container.Register <IPurchasingService>(() => purchaseService, Lifestyle.Singleton);
                Container.Register <ICacheRefresher>(() => refresher, Lifestyle.Singleton);
                Container.Register <ISubscriptionService>(() => subService, Lifestyle.Singleton);
                Container.Register <IOrderValidationService, OrderValidationService>();
                Container.Register <IPageFactory, PageFactory>(Lifestyle.Singleton);
                Container.Register <IToastService>(() => DependencyService.Get <IToastService>(), Lifestyle.Singleton);
                Container.Register <IMessagingSubscriber>(() => DependencyService.Get <IMessagingSubscriber>(), Lifestyle.Singleton);
                Container.Register <IMessagingCenter>(() => MessagingCenter.Instance, Lifestyle.Singleton);
                Container.Register <IPurchasedReportService>(() => prService, Lifestyle.Singleton);
                Container.Register <IAnalyticsService>(() => analyticsSvc, Lifestyle.Singleton);
                Container.Register <LaunchedFromPushModel>(() => App.PushModel ?? new LaunchedFromPushModel(), Lifestyle.Singleton);

                // Finish registering created caches
                Container.Register <ICache <PropertyModel> >(() => propertyCache, Lifestyle.Singleton);
                Container.Register <ICache <Models.Order> >(() => orderCache, Lifestyle.Singleton);
                Container.Register <ICache <ImageModel> >(() => imageCache, Lifestyle.Singleton);
                Container.Register <ICache <SubscriptionModel> >(() => subCache, Lifestyle.Singleton);
                Container.Register <ICache <SettingsModel> >(() => settingsCache, Lifestyle.Singleton);
                Container.Register <ICache <PurchasedReportModel> >(() => prCache, Lifestyle.Singleton);
                Container.RegisterConditional(typeof(ICache <>), typeof(MemoryCache <>), c => !c.Handled);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
                throw;
            }
        }