public async Task <IHttpActionResult> ChangePlanAsync(string id, string planId, string stripeToken = null, string last4 = null, string couponId = null) { if (String.IsNullOrEmpty(id) || !CanAccessOrganization(id)) { return(NotFound()); } if (!Settings.Current.EnableBilling) { return(Ok(ChangePlanResult.FailWithMessage("Plans cannot be changed while billing is disabled."))); } var organization = await GetModelAsync(id, false); if (organization == null) { return(Ok(ChangePlanResult.FailWithMessage("Invalid OrganizationId."))); } var plan = BillingManager.GetBillingPlan(planId); if (plan == null) { return(Ok(ChangePlanResult.FailWithMessage("Invalid PlanId."))); } if (String.Equals(organization.PlanId, plan.Id) && String.Equals(BillingManager.FreePlan.Id, plan.Id)) { return(Ok(ChangePlanResult.SuccessWithMessage("Your plan was not changed as you were already on the free plan."))); } // Only see if they can downgrade a plan if the plans are different. if (!String.Equals(organization.PlanId, plan.Id)) { var result = await _billingManager.CanDownGradeAsync(organization, plan, CurrentUser); if (!result.Success) { return(Ok(result)); } } var customerService = new StripeCustomerService(Settings.Current.StripeApiKey); var subscriptionService = new StripeSubscriptionService(Settings.Current.StripeApiKey); try { // If they are on a paid plan and then downgrade to a free plan then cancel their stripe subscription. if (!String.Equals(organization.PlanId, BillingManager.FreePlan.Id) && String.Equals(plan.Id, BillingManager.FreePlan.Id)) { if (!String.IsNullOrEmpty(organization.StripeCustomerId)) { var subs = await subscriptionService.ListAsync(new StripeSubscriptionListOptions { CustomerId = organization.StripeCustomerId }); foreach (var sub in subs.Where(s => !s.CanceledAt.HasValue)) { await subscriptionService.CancelAsync(sub.Id); } } organization.BillingStatus = BillingStatus.Trialing; organization.RemoveSuspension(); } else if (String.IsNullOrEmpty(organization.StripeCustomerId)) { if (String.IsNullOrEmpty(stripeToken)) { return(Ok(ChangePlanResult.FailWithMessage("Billing information was not set."))); } organization.SubscribeDate = SystemClock.UtcNow; var createCustomer = new StripeCustomerCreateOptions { SourceToken = stripeToken, PlanId = planId, Description = organization.Name, Email = CurrentUser.EmailAddress }; if (!String.IsNullOrWhiteSpace(couponId)) { createCustomer.CouponId = couponId; } var customer = await customerService.CreateAsync(createCustomer); organization.BillingStatus = BillingStatus.Active; organization.RemoveSuspension(); organization.StripeCustomerId = customer.Id; if (customer.Sources.TotalCount > 0) { organization.CardLast4 = customer.Sources.Data.First().Card.Last4; } } else { var update = new StripeSubscriptionUpdateOptions { PlanId = planId }; var create = new StripeSubscriptionCreateOptions(); bool cardUpdated = false; if (!String.IsNullOrEmpty(stripeToken)) { update.Source = stripeToken; create.Source = stripeToken; cardUpdated = true; } var subscriptionList = await subscriptionService.ListAsync(new StripeSubscriptionListOptions { CustomerId = organization.StripeCustomerId }); var subscription = subscriptionList.FirstOrDefault(s => !s.CanceledAt.HasValue); if (subscription != null) { await subscriptionService.UpdateAsync(subscription.Id, update); } else { await subscriptionService.CreateAsync(organization.StripeCustomerId, planId, create); } await customerService.UpdateAsync(organization.StripeCustomerId, new StripeCustomerUpdateOptions { Email = CurrentUser.EmailAddress }); if (cardUpdated) { organization.CardLast4 = last4; } organization.BillingStatus = BillingStatus.Active; organization.RemoveSuspension(); } BillingManager.ApplyBillingPlan(organization, plan, CurrentUser); await _repository.SaveAsync(organization, o => o.Cache()); await _messagePublisher.PublishAsync(new PlanChanged { OrganizationId = organization.Id }); } catch (Exception e) { _logger.Error().Exception(e).Message("An error occurred while trying to update your billing plan: " + e.Message).Critical().Identity(CurrentUser.EmailAddress).Property("User", CurrentUser).SetActionContext(ActionContext).Write(); return(Ok(ChangePlanResult.FailWithMessage(e.Message))); } return(Ok(new ChangePlanResult { Success = true })); }
public override async Task HandleItemAsync(WorkItemContext context) { var wi = context.GetData <RemoveOrganizationWorkItem>(); using (Log.BeginScope(new ExceptionlessState().Organization(wi.OrganizationId))) { Log.LogInformation("Received remove organization work item for: {organization}", wi.OrganizationId); await context.ReportProgressAsync(0, "Starting deletion...").AnyContext(); var organization = await _organizationRepository.GetByIdAsync(wi.OrganizationId).AnyContext(); if (organization == null) { await context.ReportProgressAsync(100, "Organization deleted").AnyContext(); return; } await context.ReportProgressAsync(10, "Removing subscriptions").AnyContext(); if (!String.IsNullOrEmpty(organization.StripeCustomerId)) { Log.LogInformation("Canceling stripe subscription for the organization {OrganizationName} with Id: {organization}.", organization.Name, organization.Id); var subscriptionService = new StripeSubscriptionService(Settings.Current.StripeApiKey); var subscriptions = (await subscriptionService.ListAsync(new StripeSubscriptionListOptions { CustomerId = organization.StripeCustomerId }).AnyContext()).Where(s => !s.CanceledAt.HasValue); foreach (var subscription in subscriptions) { await subscriptionService.CancelAsync(subscription.Id, new StripeSubscriptionCancelOptions()).AnyContext(); } } await context.ReportProgressAsync(20, "Removing users").AnyContext(); var users = await _userRepository.GetByOrganizationIdAsync(organization.Id).AnyContext(); foreach (var user in users.Documents) { // delete the user if they are not associated to any other organizations and they are not the current user if (user.OrganizationIds.All(oid => String.Equals(oid, organization.Id)) && !String.Equals(user.Id, wi.CurrentUserId)) { Log.LogInformation("Removing user {user} as they do not belong to any other organizations.", user.Id); await _userRepository.RemoveAsync(user.Id).AnyContext(); } else { Log.LogInformation("Removing user {user} from organization {OrganizationName} with Id: {organization}", user.Id, organization.Name, organization.Id); user.OrganizationIds.Remove(organization.Id); await _userRepository.SaveAsync(user, o => o.Cache()).AnyContext(); } } await context.ReportProgressAsync(30, "Removing tokens").AnyContext(); await _tokenRepository.RemoveAllByOrganizationIdAsync(organization.Id).AnyContext(); await context.ReportProgressAsync(40, "Removing web hooks").AnyContext(); await _webHookRepository.RemoveAllByOrganizationIdAsync(organization.Id).AnyContext(); await context.ReportProgressAsync(50, "Removing projects").AnyContext(); var projects = await _projectRepository.GetByOrganizationIdAsync(organization.Id).AnyContext(); if (wi.IsGlobalAdmin && projects.Total > 0) { int completed = 1; foreach (var project in projects.Documents) { using (Log.BeginScope(new ExceptionlessState().Organization(wi.OrganizationId).Project(project.Id))) { Log.LogInformation("Resetting all project data for project {ProjectName} with Id: {project}.", project.Name, project.Id); await _eventRepository.RemoveAllByProjectIdAsync(organization.Id, project.Id).AnyContext(); await _stackRepository.RemoveAllByProjectIdAsync(organization.Id, project.Id).AnyContext(); await context.ReportProgressAsync(CalculateProgress(projects.Total, completed++, 51, 89), "Removing projects...").AnyContext(); } } Log.LogInformation("Deleting all projects for organization {OrganizationName} with Id: {organization}.", organization.Name, organization.Id); await _projectRepository.RemoveAsync(projects.Documents).AnyContext(); } Log.LogInformation("Deleting organization {OrganizationName} with Id: {organization}.", organization.Name, organization.Id); await context.ReportProgressAsync(90, "Removing organization").AnyContext(); await _organizationRepository.RemoveAsync(organization.Id).AnyContext(); await context.ReportProgressAsync(100, "Organization deleted").AnyContext(); } }