/// <summary> /// Gets the recurring charge asynchronous. /// </summary> /// <param name="myShopifyDomain">The myshopify URL.</param> /// <param name="shopifyAccessToken">The shopify access token.</param> /// <param name="chargeId">The charge identifier.</param> /// <param name="fields">The fields.</param> /// <returns></returns> public async Task <ShopifyRecurringChargeObject> GetRecurringChargeAsync(string myShopifyDomain, string shopifyAccessToken, long chargeId, string fields = null) { _CheckmyShopifyDomain(myShopifyDomain); _CheckShopAccessToken(shopifyAccessToken); RecurringChargeService service = new RecurringChargeService(myShopifyDomain, shopifyAccessToken); _Logger.LogInformation($"Retriving recurring charge with id ='{chargeId}'"); var data = await service.GetAsync(chargeId, fields); var ret = new ShopifyRecurringChargeObject() { CancelledOn = data.CancelledOn, BillingOn = data.BillingOn, ActivatedOn = data.ActivatedOn, CappedAmount = data.CappedAmount, ConfirmationUrl = data.ConfirmationUrl, CreatedAt = data.CreatedAt, Id = data.Id, Name = data.Name, Price = data.Price, ReturnUrl = data.ReturnUrl, Status = data.Status, Terms = data.Terms, Test = data.Test, TrialDays = data.TrialDays, TrialEndsOn = data.TrialEndsOn, UpdatedAt = data.UpdatedAt }; _Logger.LogInformation($"Found recurring charge '{ret.Id} - {ret.Name} - {ret.Price} - {(ret.Test.Value ? "is_test" : "not_test")} - {ret.TrialDays} days - {ret.ReturnUrl}'."); return(ret); }
/// <summary> /// Generates a view with basic profile information (such as billing date, trial expiry, plan name etc tec) /// </summary> /// <returns></returns> public virtual async Task <ActionResult> Index() { Logger.LogInformation("Getting user to display his profile."); var me = await AppUserCache.GetLoggedOnUser(); Logger.LogInformation("Found user is {@user}", me); Logger.LogInformation("Getting plan data."); var plan = Plans[me.GetPlanId()]; Logger.LogInformation("Found plan is {@plan}", plan); Logger.LogInformation("Preparing to get recurring charge info."); ShopifyRecurringChargeObject recChargeInfo = null; bool getFrmApi = (me.IsAdmin && me.BillingIsConnected) ? false : true; if (!getFrmApi) { Logger.LogInformation("User is admin and billing is connected so recurring charge info will NOT be loaded."); } else { Logger.LogInformation("Recurring charge info will be read from shopify API."); } if (getFrmApi) { try { Logger.LogInformation("Requesting recurring charge infor from shopify."); recChargeInfo = await ShopifyAPI.GetRecurringChargeAsync(me.MyShopifyDomain, me.ShopifyAccessToken, me.ShopifyChargeId.Value); Logger.LogInformation("Recieved recurring charge info {@info}.", recChargeInfo); } catch (Exception ex) { recChargeInfo = null; Logger.LogError("Error occurred while reading recurring charge info from shopify.", ex); } } Logger.LogInformation($"My profile index view nmae is {this.Views.MyProfile.Index}."); return(View(this.Views.MyProfile.Index, new MyProfileViewModel() { Me = me, MyPlan = plan, ChargeData = recChargeInfo })); }
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> 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; } } }
/// <summary> /// Creates the recurring charge asynchronously. /// </summary> /// <param name="myShopifyDomain">My shopify URL.</param> /// <param name="shopifyAccessToken">The shopify access token.</param> /// <param name="charge">Valid charge object.</param> /// <returns></returns> public async Task <ShopifyRecurringChargeObject> CreateRecurringChargeAsync(string myShopifyDomain, string shopifyAccessToken, ShopifyRecurringChargeObject charge) { _CheckmyShopifyDomain(myShopifyDomain); _CheckShopAccessToken(shopifyAccessToken); _Logger.LogInformation($"Creating recurring charge '{charge.Name} - {charge.Price} - {(charge.Test.Value ? "is_test" : "not_test")} - {charge.TrialDays} days - {charge.ReturnUrl}'."); RecurringChargeService service = new RecurringChargeService(myShopifyDomain, shopifyAccessToken); var ret = await service.CreateAsync(new RecurringCharge() { Name = charge.Name, Price = charge.Price, Terms = charge.Terms, ReturnUrl = charge.ReturnUrl, TrialDays = charge.TrialDays, Test = charge.Test }); _Logger.LogInformation($"Done creating '{ret.Name}' with id = {ret.Id}."); return(new ShopifyRecurringChargeObject() { CancelledOn = ret.CancelledOn, BillingOn = ret.BillingOn, ActivatedOn = ret.ActivatedOn, CappedAmount = ret.CappedAmount, ConfirmationUrl = ret.ConfirmationUrl, CreatedAt = ret.CreatedAt, Id = ret.Id, Name = ret.Name, Price = ret.Price, ReturnUrl = ret.ReturnUrl, Status = ret.Status, Terms = ret.Terms, Test = ret.Test, TrialDays = ret.TrialDays, TrialEndsOn = ret.TrialEndsOn, UpdatedAt = ret.UpdatedAt }); }
public void index_should_return_valid_view_for_connected_billing(bool isAdmin) { //arrange InitAllMocks(); userCaching.Setup(x => x.GetLoggedOnUser(false)).ReturnsAsync(new AppUser(new AspNetUser() { Id = "1", PlanId = 1, ShopifyChargeId = 1 /*always connected billing*/ }, isAdmin)); planReader.Setup(x => x[1]).Returns(new PlanAppModel() { Id = 1 }); var obj = new ShopifyRecurringChargeObject() { Id = 12345, }; shopifyApi.Setup(x => x.GetRecurringChargeAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <long>(), It.IsAny <string>())).ReturnsAsync(obj); //act var c = InitController(); var result = c.Index().Result; //assert if (isAdmin == false)/*because admin==true && billing is always connected, in that situation we will not be computed*/ { shopifyApi.Verify(x => x.GetRecurringChargeAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <long>(), It.IsAny <string>())); } //shopifyApi.VerifyAll(); planReader.VerifyAll(); userCaching.VerifyAll(); Assert.NotNull(result); Assert.IsType <ViewResult>(result); var vResult = (ViewResult)result; Assert.NotNull(vResult.Model); Assert.IsType <MyProfileViewModel>(vResult.Model); var model = (MyProfileViewModel)vResult.Model; Assert.NotNull(model.Me); Assert.NotNull(model.MyPlan); if (isAdmin == false) { Assert.NotNull(model.ChargeData); } if (isAdmin == true) { Assert.Null(model.ChargeData); } Assert.Equal("1", model.Me.Id); Assert.Equal(1, model.MyPlan.Id); if (isAdmin == false) { Assert.Equal(12345, model.ChargeData.Id); } }
public void OnAuthorization(AuthorizationFilterContext filterContext) { //already logged in by Identity now check subscription if (filterContext.Result == null) { _Logger.LogInformation("Starting subscription check."); var context = filterContext.HttpContext; _Logger.LogInformation("Getting current user."); AppUser currentUser = _UserCache.GetLoggedOnUser().Result; if (currentUser == null) { _Logger.LogError("User must be logged on before checking subscription.Redirecting to login page."); //throw new Exception("Subscription check must be done on logged on user. But current user is found null."); filterContext.Result = new RedirectToActionResult(ACCOUNT_ACTIONS.Login.ToString(), _Settings.GetAccountControllerName(), new { }); } else { _Logger.LogInformation($"Current user is '{currentUser.MyShopifyDomain}'"); //admin must have access token atleast if (currentUser.ShopIsConnected == false /*&& !currentUser.IsAdmin*/) { _Logger.LogWarning($"User '{currentUser.MyShopifyDomain}' has no shopify access token. Charge status check cannot be done on diconnected shop.Redirecting to '{_Settings.GetShopifyControllerName()}/{SHOPIFY_ACTIONS.HandShake.ToString()}'."); filterContext.Result = _CreateRedirectResult(_Settings.GetShopifyControllerName(), SHOPIFY_ACTIONS.HandShake.ToString()); } //billing connected or disconnected, for admin it is never checked else if (currentUser.BillingIsConnected == false && !currentUser.IsAdmin) { _Logger.LogWarning($"User '{currentUser.MyShopifyDomain}' billing charge id is null.Charge status check cannot be done on null charge id.Redirecting to '{_Settings.GetShopifyControllerName()}/{SHOPIFY_ACTIONS.ChoosePlan.ToString()}'."); filterContext.Result = _CreateRedirectResult(_Settings.GetShopifyControllerName(), SHOPIFY_ACTIONS.ChoosePlan.ToString()); } else { ShopifyRecurringChargeObject chargeStatus = null; //for admin user if no billing charge id all good, but if theres one then we will look into it if (currentUser.IsAdmin) { _Logger.LogInformation($"Skipping charge status check because user '{currentUser.MyShopifyDomain}' is admin."); chargeStatus = new ShopifyRecurringChargeObject() { Status = SHOPIFY_CHARGE_STATUS.active.ToString() }; } else { _Logger.LogInformation($"Checking charge status for user '{currentUser.MyShopifyDomain}'."); try { chargeStatus = Task.Run(() => _ShopifyApi.GetRecurringChargeAsync(currentUser.MyShopifyDomain, currentUser.ShopifyAccessToken, currentUser.ShopifyChargeId.Value)).Result; } catch (Exception ex) { _Logger.LogError($"Error occurred duing GetRecurringChargeAsync() call.{ex.Message}.{ex.StackTrace}"); throw ex; } } if (chargeStatus.Status == SHOPIFY_CHARGE_STATUS.accepted.ToString() || chargeStatus.Status == SHOPIFY_CHARGE_STATUS.active.ToString()) { _Logger.LogInformation($"Require subscription passed for user '{currentUser.MyShopifyDomain}'"); } else { _Emailer.InActiveChargeIdDetectedAsync(currentUser, chargeStatus.Status); if (chargeStatus.Status == SHOPIFY_CHARGE_STATUS.declined.ToString() || chargeStatus.Status == SHOPIFY_CHARGE_STATUS.expired.ToString() || chargeStatus.Status == SHOPIFY_CHARGE_STATUS.pending.ToString()) { _Logger.LogWarning($"Require subscription did not pass for user '{currentUser.MyShopifyDomain}'"); _Logger.LogWarning($"User '{currentUser.MyShopifyDomain}' has declined/expired/pending charge status."); _Logger.LogWarning($"Unsetting charge info for user '{currentUser.MyShopifyDomain}'."); UserDbServiceHelper.UnSetUserChargeInfo(_UserDbService, currentUser.Id); _Logger.LogWarning($"Removing user '{currentUser.MyShopifyDomain}' from cache."); _UserCache.ClearLoggedOnUser();//resset cache so that next try makes BillingIsConnected = false var handShakeAction = SHOPIFY_ACTIONS.HandShake.ToString(); _Logger.LogWarning($"Redirecting user '{currentUser.MyShopifyDomain}' to '{_Settings.GetShopifyControllerName()}/{handShakeAction}'."); filterContext.Result = _CreateRedirectResult(_Settings.GetShopifyControllerName(), handShakeAction); } else if (chargeStatus.Status == SHOPIFY_CHARGE_STATUS.frozen.ToString()) { _Logger.LogError($"User '{currentUser.MyShopifyDomain}' has frozen shopify store account. Throwing error."); throw new UnauthorizedAccessException("Your shopify account is frozen.Once shopify unfreezes your store account you will be able to use this app again."); } } } } } }