public virtual async Task <IActionResult> Error() { var exception = HttpContext.Features.Get <IExceptionHandlerFeature>(); ErrorViewModel model = new ErrorViewModel() { Exception = exception?.Error, GeneralMessage = exception?.Error.Message, RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier, HelpMessage = "Please contact support if it keeps happening.", HelpLinkHref = "/", }; //these following lines might throw error, and we do not want error while dealing with one alraedy. try { model.SupportEmail = Settings.GetAppSupportEmailAddress(); } catch (System.Exception) { } try { model.ShowExceptionDetails = UserInContextHelper.IsCurrentUserIdAdmin(HttpContext); } catch (System.Exception) { } try { Logger.LogCritical(exception?.Error, "Critical Error Occured."); } catch (System.Exception) { } return(View(Views.ErrorPage, model)); }
/// <summary> /// Gets the logged on user data from memory cache. /// </summary> /// <param name="refresh">If set to <c>true</c>, then even if the cache data is available it will still call the <see cref="SetLoggedOnUserInCache"/> method</param> /// <returns> /// <see cref="Exico.Shopify.Data.Domain.AppModels.AppUser"/> /// </returns> public async Task <AppUser> GetLoggedOnUser(bool refresh = false) { AppUser cacheData = null; if (_HttpContextAccessor.HttpContext.User.Identity.IsAuthenticated) { var userId = UserInContextHelper.GetCurrentUserId(_HttpContextAccessor.HttpContext); var key = GetUserCacheKey(userId); if (_Cache.TryGetValue <AppUser>(key, out cacheData)) { _Logger.LogInformation($"Data found in cache for key = {key}"); if (refresh) { _Logger.LogInformation($"Refreshing cache data for key = {key} becuase refresh is requested."); cacheData = await SetLoggedOnUserInCache(); } } else { _Logger.LogWarning($"Missing cache data for logged on user for key = {key}. Save logged on user data in cache now."); cacheData = await SetLoggedOnUserInCache(); } } else { _Logger.LogInformation("Returning null because user is not logged in."); } return(cacheData); }
/// <summary> /// Gets the logged on user's cache key. /// </summary> /// <returns>The cache key </returns> public string GetLoggedOnUserCacheKey() { if (_HttpContextAccessor.HttpContext.User.Identity.IsAuthenticated) { return(GetUserCacheKey(UserInContextHelper.GetCurrentUserId(_HttpContextAccessor.HttpContext))); } else { _Logger.LogInformation("Returning null as current logged on users cache key becuase user is not logged on."); return(null); } }
/// <summary> /// Clears the logged on user data from cache. /// It can automatically determins the cach key for the current logged in user. /// </summary> public void ClearLoggedOnUser() { if (_HttpContextAccessor.HttpContext.User.Identity.IsAuthenticated) { var userId = UserInContextHelper.GetCurrentUserId(_HttpContextAccessor.HttpContext); _Logger.LogInformation($"Clearning cache for user id = {userId}"); ClearUser(userId); } else { _Logger.LogInformation("Didn't clear anything in cache, because user is not logged in."); } }
/// <summary> /// Gets a context as <see cref="LoggingContext"/> data class that has information about logged in user and store. /// </summary> /// <param name="context"></param> /// <param name="scope"></param> /// <returns></returns> public async Task <LoggingContext> GetLoggingContext(HttpContext context, IServiceScope scope) { var loggedInStatus = UserInContextHelper.IsLoggedIn(context); if (!loggedInStatus) { return new LoggingContext() { LoggedIn = false, IP = context.Connection.RemoteIpAddress.ToString() } } ; else { var caching = scope.ServiceProvider.GetService <IUserCaching>(); var planReader = scope.ServiceProvider.GetService <IPlansReader>(); var user = await caching.GetLoggedOnUser(); if (user != null) { var plan = planReader[user.PlanId.HasValue ? user.PlanId.Value : 0]; return(new LoggingContext() { LoggedIn = true, AccessToken = user.ShopifyAccessToken, Email = user.Email, IP = context.Connection.RemoteIpAddress.ToString(), RequestPath = context.Request.Path, Shop = user.MyShopifyDomain, ChargeId = user.ShopifyChargeId, PlanId = user.PlanId, PlanName = plan?.Name, UserId = user.Id, }); } else { //somethings not right, but let us cope with the situation return(new LoggingContext() { LoggedIn = true, IP = context.Connection.RemoteIpAddress.ToString(), UserId = UserInContextHelper.GetCurrentUserId(context), Shop = context.User.Identity.Name, Comment = "User is logged in but not present in the cache. Deleted from the db??" }); } } }
public void Get_Current_User_Id_Return_Null_If_Not_Authenticated(bool userIsLoggedOn) { InitServiceProvider(userIsLoggedOn); var contextAccessor = serviceProvider.GetRequiredService <IHttpContextAccessor>(); if (userIsLoggedOn) { Assert.Equal(UserId, UserInContextHelper.GetCurrentUserId(contextAccessor.HttpContext)); } else { Assert.Null(UserInContextHelper.GetCurrentUserId(contextAccessor.HttpContext)); } }
/// <summary> /// Gets a copy of the user data the from DB by logged on user id and sets it into memory cache. /// </summary> /// <returns> /// <see cref="Exico.Shopify.Data.Domain.AppModels.AppUser"/> /// </returns> public async Task <AppUser> SetLoggedOnUserInCache() { AppUser cacheData = null; if (_HttpContextAccessor.HttpContext.User.Identity.IsAuthenticated) { AspNetUser user = await _Store.GetClaimedUserData(_HttpContextAccessor.HttpContext.User); if (user != null) { var cKey = GetUserCacheKey(user.Id); _Logger.LogInformation($"Setting logged on user in to cache with key = '{cKey}'."); cacheData = new AppUser(user, UserInContextHelper.IsCurrentUserIdAdmin(_HttpContextAccessor.HttpContext)); _Cache.Set <AppUser>(cKey, cacheData, new MemoryCacheEntryOptions() { SlidingExpiration = TimeSpan.FromMinutes(USER_CACHE_EXPIRE_MINUTES), Priority = CacheItemPriority.High, }); _Logger.LogInformation("Returning cached logged in user {@user}.", cacheData); } else { var errMsg = $"User {_HttpContextAccessor.HttpContext.User.Identity.Name} is logged in user but identity is not found in our database."; _Logger.LogError(new Exception(errMsg), errMsg + "{@data}", _HttpContextAccessor.HttpContext.User.Identity.Name); _Logger.LogWarning("Force signing out."); await _SignInManager.SignOutAsync(); _Logger.LogInformation("Redirecting to / "); _HttpContextAccessor.HttpContext.Response.Redirect("/"); }; } else { _Logger.LogInformation("Returning null because user is not logged in."); } return(cacheData); }
public virtual async Task <IActionResult> SelectedPlan(int planId) { using (Logger.BeginScope(new { PlanId = planId })) { try { Logger.LogInformation("Getting user"); var user = await UserDbServiceHelper.GetAppUserByIdAsync(UserDbService, UserInContextHelper.GetCurrentUserId(HttpContext)); string domain = user.MyShopifyDomain; string token = user.ShopifyAccessToken; /*user plan id = 0 means that customer is new*/ int userPlanId = user.GetPlanId(); Logger.LogInformation($"Got user.User ID '{user.Id}', domain '{user.MyShopifyDomain}', token '{user.ShopifyAccessToken}' and Plan Id '{user.PlanId}'."); //privileged ip holders can downgrade or upgrade plan, others are upgrade only var validUpgrade = planId >= userPlanId; var priviledgedUser = IPAddressHelper.IsCurrentUserIpPrivileged(HttpContext, Settings); Logger.LogInformation($"Selected is a valid upgrade : {validUpgrade}"); Logger.LogInformation($"Selector's IP is priviledged: {priviledgedUser}"); if (validUpgrade || priviledgedUser) { Logger.LogInformation("Plan selection is approved."); var plan = PlanReader[planId]; if (plan != null && plan.Id > 0) { Logger.LogInformation($"Found plan for the selected ID. Plan Name '{plan.Name}'."); var charge = new ShopifyRecurringChargeObject() { Name = plan.Name, Price = plan.Price, TrialDays = plan.TrialDays, Test = plan.IsTest, ReturnUrl = ShopifyUrlHelper.GetChargeResultHandlerUrl(Settings), }; try { Logger.LogInformation("Creating recurring charge via api for selected plan."); charge = await ShopifyAPI.CreateRecurringChargeAsync(domain, token, charge); Logger.LogInformation($"Successfully created recurring charge. Redirecting to confirmation URL '{charge.ConfirmationUrl}'."); return(Redirect(charge.ConfirmationUrl)); } catch (Exception ex) { Logger.LogError(ex, $"Failed creating recurring charge for the selected plan.Redirecting to '{SHOPIFY_ACTIONS.ChoosePlan.ToString()}' action."); WebMsg.AddTempDanger(this, "Could not create a recurring charge record/confirmation url via shopify api. Please try again.", false, false); return(RedirectToAction(SHOPIFY_ACTIONS.ChoosePlan.ToString(), Settings.GetShopifyControllerName())); } } } //if we are here then it is an invalid plan Logger.LogWarning($"Selection is not approved.Redirecting to '{SHOPIFY_ACTIONS.ChoosePlan.ToString()}' adction"); WebMsg.AddTempDanger(this, "Invalid Plan Selected", false, false); return(RedirectToAction(SHOPIFY_ACTIONS.ChoosePlan.ToString(), Settings.GetShopifyControllerName())); } catch (Exception ex) { Logger.LogWarning("Error occurred while executing SelectedPlan())"); LogGenericError(ex); throw ex; } } }
public virtual async Task <IActionResult> ChoosePlan() { try { Logger.LogInformation("Getting user."); var user = await UserDbServiceHelper.GetAppUserByIdAsync(UserDbService, UserInContextHelper.GetCurrentUserId(HttpContext)); var planStartId = user.GetPlanId(); ViewBag.PrePlan = planStartId > 0 ? true : false; Logger.LogInformation($"Got user. User ID '{user.Id}', existing plan/plan start ID '{planStartId}'."); //Dev plans are only included if ip is privileged and user is admin var ipIsPriviledged = IPAddressHelper.IsCurrentUserIpPrivileged(HttpContext, Settings); var isAdmin = user.IsAdmin;; Logger.LogInformation($"User IP is priviledged : '{ipIsPriviledged}'."); Logger.LogInformation($"User is admin : '{isAdmin}'."); bool includeDev = ipIsPriviledged && isAdmin; Logger.LogInformation($"Dev plans are being included : '{includeDev}'"); Logger.LogInformation("Listing plans now."); var plans = PlanReader.GetAvailableUpgrades(planStartId, includeDev); Logger.LogInformation($"Total '{plans.Count}' have been listed."); Logger.LogInformation($"Choose plan view name is {Views.Shopify.ChoosePlan}."); return(View(Views.Shopify.ChoosePlan, plans)); } catch (Exception ex) { Logger.LogWarning("Error occurred while executing ChoosePlan())"); LogGenericError(ex); throw ex; } }
public virtual async Task <IActionResult> ChargeResult(string shop, long charge_id) { using (Logger.BeginScope(new { Shop = shop, ChargeId = charge_id })) { try { Logger.LogInformation("Getting current user."); var user = await UserDbServiceHelper.GetAppUserByIdAsync(UserDbService, UserInContextHelper.GetCurrentUserId(HttpContext)); Logger.LogInformation($"Found user. Id is '{user.Id}'"); //to detect if its an upgrade we need users previous plan id, 0 means no previous plan. Logger.LogInformation($"Getting previous plan info, if any."); var previousPlan = user.GetPlanId(); Logger.LogInformation($"Previous plan ID is set to '{previousPlan}'."); PlanAppModel newPlan = null; ShopifyRecurringChargeObject charge = null; try { Logger.LogInformation($"Retriving recurring charge info (before activation). Access token '{user.ShopifyAccessToken}' and charge id '{charge_id}'."); charge = await ShopifyAPI.GetRecurringChargeAsync(user.MyShopifyDomain, user.ShopifyAccessToken, charge_id); Logger.LogInformation($"Successfully retrived recurring charge info (before activation).Id is '{charge.Id}'.Associated plan name '{charge.Name}' and price '{charge.Price}'."); } catch (Exception ex) { Logger.LogError(ex, "Error retriving recurring charge info (before activation)."); WebMsg.AddTempDanger(this, "Could not retrive charge status by charge id via shopify api (before activation)."); return(RedirectToAction(SHOPIFY_ACTIONS.ChoosePlan.ToString(), Settings.GetShopifyControllerName())); } newPlan = PlanReader[charge.Name]; if (newPlan != null) { Logger.LogInformation($"New plan ID is set to '{newPlan.Id}'."); Logger.LogInformation($"Recurring charge status (before activation) is '{charge.Status}'."); if (charge.Status == "accepted") { //Lets activate the charge try { Logger.LogInformation("Trying to activate the recurring charge."); await ShopifyAPI.ActivateRecurringChargeAsync(user.MyShopifyDomain, user.ShopifyAccessToken, charge_id); Logger.LogInformation("Recurring charge activationon is done."); } catch (Exception ex) { Logger.LogError(ex, $"Recurring charge activation failed.Redirecting to '{SHOPIFY_ACTIONS.ChoosePlan.ToString()}' action."); WebMsg.AddTempDanger(this, "Could not activate the recurring charge via shopify api.Please try again."); return(RedirectToAction(SHOPIFY_ACTIONS.ChoosePlan.ToString(), Settings.GetShopifyControllerName())); } //Lets check if we were sucessful activating the charge try { Logger.LogInformation("Checking recurring charge status after activation."); charge = await ShopifyAPI.GetRecurringChargeAsync(user.MyShopifyDomain, user.ShopifyAccessToken, charge_id); } catch (Exception ex) { Logger.LogError(ex, "Error getting recurring charge status after activation."); WebMsg.AddTempDanger(this, "Could not retrieve charge status by id via shopify api (after activation). Please try again or contact support"); return(RedirectToAction(SHOPIFY_ACTIONS.ChoosePlan.ToString(), Settings.GetShopifyControllerName())); } Logger.LogInformation($"Recurring Charge Status after activation is '{charge.Status}'."); //if we were succesful if (charge.Status == "active") { Logger.LogInformation($"Saving user payment information. User id '{user.Id}', charge id '{charge_id}' , plan id '{newPlan.Id}' and billing on '{charge.BillingOn}'."); var updateResult = UserDbServiceHelper.SetUsersChargeInfo(UserDbService, user.Id, charge_id, newPlan.Id, charge.BillingOn); if (!updateResult) { Logger.LogCritical($"Could not save user payment information.Redirecting to '{SHOPIFY_ACTIONS.ChoosePlan.ToString()}' action."); await Emailer.UserPaymentInfoCouldNotBeSavedAsync(user, charge_id, charge.Name); //this.LogCoreException(new CoreException(CoreErrorType.APP_COULD_NOT_SAVE_CHARGE_INFO, errMessage + $".Activated Charge Id {charge.Id}", user, shop)); WebMsg.AddTempDanger(this, "Could not save your payment confirmation in our db.Please try again."); return(RedirectToAction(SHOPIFY_ACTIONS.ChoosePlan.ToString(), Settings.GetShopifyControllerName())); } else { Logger.LogInformation("Succesfully saved user payment information."); Logger.LogInformation("Refreshing user cache data now."); user = await UserCache.GetLoggedOnUser(true); Logger.LogInformation("Now detecting installation type."); if (previousPlan > 0 && previousPlan != newPlan.Id)//its an upgrade { Logger.LogInformation("Installation was an upgrade type for existing store. Redirecting to RedirectAfterSuccessfulUpgrade()."); await UserChangedPlan(user, newPlan.Id); //handle upgrade event return(RedirectAfterSuccessfulUpgrade(charge.Name)); //now redirect } else//new installation { Logger.LogInformation("Installation was for a new store."); Logger.LogInformation("Now handling post installation tasks by calling DoPostInstallationTasks()."); await DoPostInstallationTasks(user);//handle new installation event Logger.LogInformation("Done handling post installation tasks. "); await SendEmailsOnSuccessfullInstallation(user);//send emails Logger.LogInformation($"Now processing all webhooks defined in the appsettings.json by calling ProcessWebHooksCreation()."); await ProcessWebhooks(user); Logger.LogInformation("Done processing webhooks defined in appsettings.json."); Logger.LogInformation("Now redirecting after successfull sign in."); return(RedirectAfterSuccessfullLogin());//now redirect } } } else /*if status is not active*/ { Logger.LogCritical($"SHopify could not activate the recurring charge. Redirecting to '{SHOPIFY_ACTIONS.ChoosePlan.ToString()}' action."); WebMsg.AddTempDanger(this, "Shopify did not activate the recurring payment this app requested. Please try again by choosing a plan."); return(RedirectToAction(SHOPIFY_ACTIONS.ChoosePlan.ToString(), Settings.GetShopifyControllerName())); } } else if (charge.Status == "declined") { Logger.LogCritical("Recurring charge was declined (before activation).Probably user declined payment."); Logger.LogInformation("Calling UserCancelledPayment() event."); await UserCancelledPayment(user, newPlan.Id); Logger.LogInformation("Done handling UserCancelledPayment() event."); if (user.GetPlanId() <= 0) { Logger.LogWarning("Redirecting to RedirectAfterNewUserCancelledPayment() as payment cancelled."); return(RedirectAfterNewUserCancelledPayment(user)); } else { Logger.LogWarning("Redirecting to RedirectAfterPlanChangePaymentDeclined() as payment declined"); return(RedirectAfterPlanChangePaymentDeclined()); } } else { Logger.LogCritical($"Recurring charge was not accepted by shopify (before activation). Redirecting to '{SHOPIFY_ACTIONS.ChoosePlan.ToString()}' action."); WebMsg.AddTempDanger(this, "Payment was not accepted by shopify. Please try again."); return(RedirectToAction(SHOPIFY_ACTIONS.ChoosePlan.ToString(), Settings.GetShopifyControllerName())); } } else { Logger.LogCritical($"Recurring charge's plan is not found in the loaded db plan list.Redirecting to '{SHOPIFY_ACTIONS.ChoosePlan.ToString()}' action."); WebMsg.AddTempDanger(this, "Could not retrieve plan information.Please try again."); return(RedirectToAction(SHOPIFY_ACTIONS.ChoosePlan.ToString(), Settings.GetShopifyControllerName()));//let the user try again } } catch (Exception ex) { Logger.LogWarning("Error occurred while executing ChargeResult()."); LogGenericError(ex); throw ex; } } }