예제 #1
0
        public async Task <DomainTrackingUrl> AddAsync(DomainTrackingUrl trackingUrl)
        {
            // Get subscription
            CompanySubscription subscription = await _subscriptionService.GetAsync(trackingUrl.SubscriptionId);

            // Check redirect url
            if (trackingUrl.RedirectUrl == null)
            {
                throw new ArgumentException("RedirectUrl required");
            }

            if (!string.Equals(trackingUrl.RedirectUrl.Host, subscription.SiteHostname, StringComparison.InvariantCultureIgnoreCase))
            {
                throw new BadRequestException(
                          string.Format("Client subscription {0} allows only URLs starting with {1}: {2}",
                                        subscription.Id, subscription.SiteHostname, trackingUrl.RedirectUrl));
            }

            // Add tracking url
            TrackingUrlEntity entity = _mappingEngine.Map <DomainTrackingUrl, TrackingUrlEntity>(trackingUrl);

            entity = await _trackingUrlRepository.AddAsync(entity);

            DomainTrackingUrl result = _mappingEngine.Map <TrackingUrlEntity, DomainTrackingUrl>(entity);

            result.Key = _urlShortenerService.Encode(Int64.Parse(result.Id));

            return(result);
        }
예제 #2
0
        public async Task <CompanySubscription> AddAsync(string userId, CompanySubscriptionCreateOptions options)
        {
            CompanySubscription subscription = _mapper.Map <CompanySubscriptionCreateOptions, CompanySubscription>(options);

            // Searching for company
            CompanyEntity company = await _companyRepository.FindByUserAsync(userId);

            if (company == null)
            {
                throw new NotFoundException(string.Format("Could not find company by user '{0}'", userId));
            }

            CheckCompanyAccess(company);

            // Setting defaults
            subscription.Id             = Guid.NewGuid().ToString();
            subscription.Created        = DateTime.UtcNow;
            subscription.HasTrialClicks = true;

            SubscriptionEntity subscriptionEntity = _mapper.Map <CompanySubscription, SubscriptionEntity>(subscription);

            // Updating company
            company.Subscriptions.Add(subscriptionEntity);
            await _companyRepository.UpdateAsync(company);

            return(subscription);
        }
        public async Task <HttpResponseMessage> Post(CreateSubscriptionModel model)
        {
            // Create subscription
            CompanySubscription subscription = await _subscriptionService.AddAsync(UserId, model);

            Subscription        subscriptionResponse = _mapper.Map <CompanySubscription, Subscription>(subscription);
            HttpResponseMessage response             = Request.CreateResponse(HttpStatusCode.Created, subscriptionResponse);

            return(response);
        }
        public async Task <HttpResponseMessage> Put(string id, SubscriptionModel model)
        {
            // Update subscription
            CompanySubscription subscription = await _subscriptionService.UpdateAsync(UserId, id, model);

            Subscription        profileResponse = _mapper.Map <CompanySubscription, Subscription>(subscription);
            HttpResponseMessage response        = Request.CreateResponse(HttpStatusCode.OK, profileResponse);

            return(response);
        }
        //
        // GET: /api/clients/subscriptions/{id}

        /// <summary>
        ///     Gets client subscription.
        /// </summary>
        /// <returns></returns>
        public async Task <Subscription> Get(string id)
        {
            // Get company for user
            DomainCompany company = await _companyService.FindByUserAsync(UserId);

            CompanySubscription subscription = company.Subscriptions.FirstOrDefault(s => s.Id == id);

            if (subscription == null)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, ResponseMessages.ResourceNotFound));
            }

            return(_mapper.Map <CompanySubscription, Subscription>(subscription));
        }
        public CompanyDto CreateCompany(CompanyCreateDto dto)
        {
            var repoUser = _contextManager.CreateRepositiry <IUserRepo>();
            var user     = repoUser.GetById(dto.UserId);

            var repoCompany = _contextManager.CreateRepositiry <ICompanyRepo>();
            var company     = repoCompany.GetCompanyByName(dto.Name);

            if (!(company == null))
            {
                throw new ValidationException("Company with name <" + dto.Name + "> is already exists.");
            }

            var entity = _mapper.Map <Company>(dto);

            repoCompany.Add(entity);

            _contextManager.Save();

            var repoCompUser = _contextManager.CreateRepositiry <ICompanyUserLinkRepo>();
            var companyUser  = new CompanyUserLink
            {
                CompanyId       = entity.Id,
                UserId          = user.Id,
                UserCompanyRole = (int)Model.Enums.CompanyUserRoleEnum.Owner
            };

            repoCompUser.Add(companyUser);
            _contextManager.Save();


            var repoCompSub = _contextManager.CreateRepositiry <ICompanySubRepo>();
            var companySub  = new CompanySubscription
            {
                CompanyId       = entity.Id,
                UserId          = user.Id,
                SubEndDt        = DateTime.UtcNow.Date,
                UseTrialVersion = (int)Model.Enums.CompanyTrialVersionEnum.ReadyToUseTrial
            };

            repoCompSub.Add(companySub);
            _contextManager.Save();

            return(_mapper.Map <CompanyDto>(entity));
        }
        public async Task <bool> SubscribeAsync(int companyId, string userId)
        {
            var company = await this.dbContext.FindAsync <Company>(companyId);

            if (company == null)
            {
                return(false);
            }

            var sub = new CompanySubscription
            {
                UserId    = userId,
                CompanyId = companyId
            };

            await this.dbContext.AddAsync(sub);

            await this.dbContext.SaveChangesAsync();

            return(true);
        }
예제 #8
0
        public async Task <IEnumerable <TrackingStatPerDate> > Get(string id, [FromUri] ClientSubscriptionStatUrlModel model, ODataQueryOptions <TrackingStatPerDate> options)
        {
            var validationSettings = new ODataValidationSettings
            {
                AllowedArithmeticOperators = AllowedArithmeticOperators.None,
                AllowedFunctions           = AllowedFunctions.None,
                AllowedLogicalOperators    =
                    AllowedLogicalOperators.Equal | AllowedLogicalOperators.LessThan | AllowedLogicalOperators.LessThanOrEqual | AllowedLogicalOperators.GreaterThan |
                    AllowedLogicalOperators.GreaterThanOrEqual | AllowedLogicalOperators.And,
                AllowedQueryOptions = AllowedQueryOptions.Filter
            };

            // Validating query options
            try
            {
                options.Validate(validationSettings);
            }
            catch (Exception)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ResponseMessages.InvalidQueryOptions));
            }


            // Parsing filter parameters
            DataQueryOptions filter;

            try
            {
                filter = _mapper.Map <ODataQueryOptions, DataQueryOptions>(options);
            }
            catch (AutoMapperMappingException)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ResponseMessages.InvalidQueryOptions));
            }


            // Get company for user
            DomainCompany company = await _companyService.FindBySubscriptionAsync(id);

            if (!company.Users.Contains(UserId))
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.Forbidden, ResponseMessages.Forbidden));
            }

            // Find subscription
            CompanySubscription subscription = company.Subscriptions.FirstOrDefault(s => s.Id == id);

            if (subscription == null)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, ResponseMessages.ResourceNotFound));
            }

            // Get stats
            IEnumerable <DomainTrackingStatPerDate> stats;

            try
            {
                stats = await _urlTrackingStatService.GetStatsPerDateAsync(id, model.Url, filter);
            }
            catch (NotSupportedException)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ResponseMessages.BadRequest));
            }
            catch (ArgumentException)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ResponseMessages.BadRequest));
            }

            IEnumerable <TrackingStatPerDate> results = stats.Select(r => _mapper.Map <DomainTrackingStatPerDate, TrackingStatPerDate>(r));

            return(results);
        }
예제 #9
0
 public bool SaveCompanySubscription(CompanySubscription CompanySubscription)
 {
     return(_businessInstance.SaveCompanySubscriptionInfo(CompanySubscription));
 }
예제 #10
0
        private async Task SyncSubscriptionAsync(string companyId, CompanySubscription subscription, DateTime syncDate, JObject subscriptionPlans)
        {
            if (subscription.Type == SubscriptionType.Free)
            {
                // nothing to sync
                return;
            }

            JToken planInfo = subscriptionPlans[((int)subscription.Type).ToString(CultureInfo.InvariantCulture)];

            // Check invoice period
            DateTime lastSyncDate = subscription.LastSyncDate ?? subscription.Created;
            long     syncPeriodMs = Int64.Parse((string)planInfo["syncPeriodMs"]);

            if (syncPeriodMs <= 0 || syncDate.Subtract(lastSyncDate).TotalMilliseconds < syncPeriodMs)
            {
                // its too early for invoice
                return;
            }

            // Calculate clicks for sync period
            long periodClicks = await _statService.GetTotalAsync(subscription.Id, lastSyncDate, syncDate);

            // Calculate clicks for cycle
            DateTime lastCycleDate = subscription.LastCycleDate ?? subscription.Created;
            long     cycleClicks   = await _statService.GetTotalAsync(subscription.Id, lastCycleDate, lastSyncDate);

            JToken  clickRatesInfo = planInfo["clickRates"];
            var     charges        = new List <Tuple <decimal, long, long, long?> >();
            long    clicks         = periodClicks;
            decimal lastRate       = 0;
            long    lastCount      = 0;
            long    clicksMargin   = cycleClicks;

            foreach (JToken clickRate in clickRatesInfo)
            {
                var r = clickRate as JProperty;
                if (r == null)
                {
                    continue;
                }

                long    count = Int64.Parse(r.Name);
                decimal rate  = Decimal.Parse((string)r.Value);
                if (count <= 0)
                {
                    // skip free/initial rate range
                    lastRate = rate;
                    continue;
                }

                if (clicksMargin >= count)
                {
                    // skip last bill
                    lastRate  = rate;
                    lastCount = count;
                    continue;
                }

                // calculate charge for range of clicks
                long lastRange   = Math.Max(clicksMargin, lastCount);
                long rangeClicks = lastRange + clicks < count ? clicks : count - lastRange;

                // reseting margin
                clicksMargin = 0;

                // creating charge
                if (rangeClicks > 0)
                {
                    charges.Add(new Tuple <decimal, long, long, long?>(lastRate, rangeClicks, lastCount, count));
                }


                // going to next range
                lastRate  = rate;
                lastCount = count;
                clicks   -= rangeClicks;
                if (clicks == 0)
                {
                    break;
                }
            }

            if (clicks > 0)
            {
                // using last specified rate for exceeded clicks
                charges.Add(new Tuple <decimal, long, long, long?>(lastRate, clicks, lastCount, null));
            }


            // Synchronizing
            var chargeRecords = new List <DomainBalanceHistory>();

            try
            {
                if (charges.Count > 0)
                {
                    if (charges.Sum(c => c.Item1) > 0)
                    {
                        // trial clicks ended
                        await _subscriptionService.UpdateHasTrialClicksAsync(subscription.Id, false);
                    }
                    else
                    {
                        // trial clicks
                        await _subscriptionService.UpdateHasTrialClicksAsync(subscription.Id, true);
                    }

                    foreach (var charge in charges)
                    {
                        decimal rate          = charge.Item1;
                        long    count         = charge.Item2;
                        long    lowCount      = charge.Item3;
                        long?   highCount     = charge.Item4;
                        string  packageName   = highCount.HasValue ? string.Format("{0}-{1}", lowCount, highCount) : string.Format("{0}+", lowCount);
                        var     balanceRecord = new DomainBalanceHistory
                        {
                            CompanyId   = companyId,
                            Amount      = -(rate * count),
                            Description =
                                string.Format(_config.BillingInvoiceItemDescriptionTemplate, count, subscription.SiteName, subscription.Id, subscription.Type, rate / 100, packageName, lastSyncDate,
                                              syncDate)
                        };

                        await _balanceService.AddAsync(balanceRecord);

                        chargeRecords.Add(balanceRecord);
                    }
                }

                // cycle completed
                long cyclePeriodMs = Int64.Parse((string)planInfo["cyclePeriodMs"]);
                if (cyclePeriodMs > 0 && syncDate.Subtract(lastCycleDate).TotalMilliseconds >= cyclePeriodMs)
                {
                    // updating last cycle date if cycle completed
                    await _subscriptionService.UpdateLastCycleDateAsync(subscription.Id, syncDate);

                    // enabling trial clicks
                    await _subscriptionService.UpdateHasTrialClicksAsync(subscription.Id, true);
                }

                // updating last sync date
                await _subscriptionService.UpdateLastSyncDateAsync(subscription.Id, syncDate);
            }
            catch (Exception e)
            {
                Trace.TraceError("Failed to sync balance for subscription {0} of company {1}: {2}", companyId, subscription.Id, e);

                // trying rollback
                try
                {
                    // rollback balance
                    if (chargeRecords.Count > 0)
                    {
                        foreach (DomainBalanceHistory record in chargeRecords)
                        {
                            _balanceService.DeleteAsync(record.Id).Wait();
                        }
                    }

                    // rollback subscription state
                    _subscriptionService.UpdateLastCycleDateAsync(subscription.Id, lastCycleDate).Wait();
                    _subscriptionService.UpdateHasTrialClicksAsync(subscription.Id, subscription.HasTrialClicks).Wait();
                }
                catch (Exception ex)
                {
                    Trace.TraceError("Failed to roll back subscription {0} state for company {1}. Subscription data is corrupted: {2}", subscription.Id, companyId, ex);
                }
            }
        }
예제 #11
0
 public bool SaveCompanySubscriptionInfo(CompanySubscription CompanySubscription)
 {
     return(_userDataAccess.SaveCompanySubscriptionInfo(CompanySubscription));
 }
        public async Task <HttpResponseMessage> Get(string id, ODataQueryOptions <TrackingStatPerUrl> options)
        {
            var validationSettings = new ODataValidationSettings
            {
                MaxTop = 100,
                AllowedArithmeticOperators = AllowedArithmeticOperators.None,
                AllowedFunctions           = AllowedFunctions.None,
                AllowedLogicalOperators    =
                    AllowedLogicalOperators.Equal | AllowedLogicalOperators.And,
                AllowedQueryOptions =
                    AllowedQueryOptions.Filter | AllowedQueryOptions.OrderBy | AllowedQueryOptions.Skip | AllowedQueryOptions.Top | AllowedQueryOptions.InlineCount
            };

            // Validating query options
            try
            {
                options.Validate(validationSettings);
            }
            catch (Exception)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ResponseMessages.InvalidQueryOptions));
            }


            // Parsing filter parameters
            DataQueryOptions filter;

            try
            {
                filter = _mapper.Map <ODataQueryOptions, DataQueryOptions>(options);
            }
            catch (AutoMapperMappingException)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ResponseMessages.InvalidQueryOptions));
            }

            // Get company for user
            DomainCompany company = await _companyService.FindByUserAsync(UserId);

            // Find subscription
            CompanySubscription subscription = company.Subscriptions.FirstOrDefault(s => s.Id == id);

            if (subscription == null)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, ResponseMessages.ResourceNotFound));
            }


            // Get stats
            DataResult <DomainTrackingStatPerUrl> stats;

            try
            {
                stats = await _urlTrackingStatService.GetStatsPerUrlAsync(id, filter);
            }
            catch (NotSupportedException)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ResponseMessages.BadRequest));
            }
            catch (ArgumentException)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ResponseMessages.BadRequest));
            }

            IEnumerable <TrackingStatPerUrl> results = stats.Results.Select(r => _mapper.Map <DomainTrackingStatPerUrl, TrackingStatPerUrl>(r));

            if (filter.Count)
            {
                var pageResult = new PageResult <TrackingStatPerUrl>(results, null, stats.Count);
                return(Request.CreateResponse(HttpStatusCode.OK, pageResult));
            }

            return(Request.CreateResponse(HttpStatusCode.OK, results));
        }