Beispiel #1
0
        private async Task ChargeSucceededAsync(DomainEvent billingEvent)
        {
            // Retrieve company by charge data
            DomainCharge charge = await _billingChargeService.GetAsync(new DomainCharge { Id = billingEvent.ObjectId });

            DomainCompany company = await _companyService.FindByCustomerAsync(charge.CustomerId);

            // Updating balance
            var balanceRecord = new DomainBalanceHistory
            {
                Amount      = charge.AmountInCents,
                Description = BillingMessages.ChargeSucceeded,
                CompanyId   = company.Id
            };

            await _balanceService.AddAsync(balanceRecord);

            // Notify client about payment operation result
            try
            {
                await _notificationService.SendPaymentNotificationAsync(billingEvent, company, charge);
            }
            catch (Exception e)
            {
                Trace.TraceError("Failed to send payment notification e-mail to company {0}: {1}", company.Id, e);
            }
        }
Beispiel #2
0
        public async Task <DomainCompany> AddAsync(DomainCompany company)
        {
            company.Created = DateTime.UtcNow;
            company.Email   = company.Email.ToLowerInvariant();

            CompanyEntity entity = _mapper.Map <DomainCompany, CompanyEntity>(company);

            // Creating customer in billing system
            var customerCreateOptions = new DomainCustomerCreateOptions
            {
                Email = entity.Email
            };

            DomainCustomer customer;

            try
            {
                customer = await _billingCustomerService.AddAsync(customerCreateOptions);
            }
            catch (BillingException e)
            {
                throw new BadRequestException(string.Format("Failed to register customer {0}: {1}", entity.Email, e));
            }

            entity.BillingCustomerId = customer.Id;
            entity = await _companyRepository.AddAsync(entity);

            return(_mapper.Map <CompanyEntity, DomainCompany>(entity));
        }
        public async Task <HttpResponseMessage> Put(ClientModel model)
        {
            // Change e-mail
            await _userService.ChangeEmailAsync(UserId, model.Email);

            // Update user
            var userUpdate = new UserUpdateOptions
            {
                Country  = model.Country,
                UserName = model.ContactPerson
            };

            DomainUser user = await _userService.UpdateAsync(UserId, userUpdate);

            // Update company
            var companyUpdate = new CompanyUpdateOptions
            {
                Address = model.Address,
                Country = model.Country,
                Ein     = model.Ein,
                Name    = model.CompanyName,
                Phone   = model.PhoneNumber,
                ZipCode = model.ZipCode,
                Email   = model.Email
            };

            DomainCompany company = await _companyService.UpdateByUserAsync(UserId, companyUpdate);

            Client client = _mapper.Map <Tuple <DomainUser, DomainCompany>, Client>(new Tuple <DomainUser, DomainCompany>(user, company));

            HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, client);

            return(response);
        }
Beispiel #4
0
        private async Task SyncCompanyAsync(DomainCompany company, JObject subscriptionPlans, DateTime?syncDate = null)
        {
            syncDate = syncDate ?? DateTime.UtcNow.AddMinutes(-1); // just to be sure that all transactions were committed
            List <CompanySubscription> subscriptions = company.Subscriptions.Where(s => s.State == ResourceState.Available).ToList();
            List <Task> subscriptionTasks            = subscriptions.Select(s => SyncSubscriptionAsync(company.Id, s, syncDate.Value, subscriptionPlans)).ToList();

            await Task.WhenAll(subscriptionTasks);
        }
        public async Task <IEnumerable <Subscription> > Get()
        {
            // Get company for user
            DomainCompany company = await _companyService.FindByUserAsync(UserId);

            List <CompanySubscription> activeSubscriptions = company.Subscriptions;

            return(_mapper.Map <List <CompanySubscription>, List <Subscription> >(activeSubscriptions));
        }
Beispiel #6
0
        public async Task <IEnumerable <BalanceHistory> > Get()
        {
            // Get company for user
            DomainCompany company = await _companyService.FindByUserAsync(UserId);

            IEnumerable <DomainBalanceHistory> balanceHistory = await _balanceService.QueryHistoryAsync(company.Id);

            return(_mapper.Map <IEnumerable <DomainBalanceHistory>, IEnumerable <BalanceHistory> >(balanceHistory));
        }
Beispiel #7
0
        //
        // GET: /api/clients/balance

        /// <summary>
        ///     Gets accumulative client company balance.
        /// </summary>
        /// <returns></returns>
        public async Task <Balance> Get()
        {
            // Get company for user
            DomainCompany company = await _companyService.FindByUserAsync(UserId);

            decimal balance = await _balanceService.GetBalanceAsync(company.Id);

            return(new Balance {
                Amount = balance, Date = DateTime.UtcNow
            });
        }
        //
        // 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));
        }
        //
        // GET: /api/clients

        /// <summary>
        ///     Gets client profile information.
        /// </summary>
        /// <returns>Client profile information.</returns>
        public async Task <HttpResponseMessage> Get()
        {
            // Get profile
            DomainUser user = await _userService.GetAsync(UserId);

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

            Client client = _mapper.Map <Tuple <DomainUser, DomainCompany>, Client>(new Tuple <DomainUser, DomainCompany>(user, company));

            HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, client);

            response.SetLastModifiedDate(user.Modified);

            return(response);
        }
Beispiel #10
0
        private async Task ChargeFailedAsync(DomainEvent billingEvent)
        {
            // Retrieve company by charge data
            DomainCharge charge = await _billingChargeService.GetAsync(new DomainCharge { Id = billingEvent.ObjectId });

            DomainCompany company = await _companyService.FindByCustomerAsync(charge.CustomerId);

            // Notify client about payment operation result
            try
            {
                await _notificationService.SendPaymentNotificationAsync(billingEvent, company, charge);
            }
            catch (Exception e)
            {
                Trace.TraceError("Failed to send payment notification e-mail to company {0}: {1}", company.Id, e);
            }
        }
Beispiel #11
0
        private async Task ChargeRefundedAsync(DomainEvent billingEvent)
        {
            // Get charge info
            DomainCharge charge = await _billingChargeService.GetAsync(new DomainCharge { Id = billingEvent.ObjectId });

            var refunds = billingEvent.Object["refunds"] as JArray;

            if (refunds != null)
            {
                // refund can be partial, accounting only last refund
                JToken lastRefund    = refunds.Last;
                int    refundInCents = Int32.Parse(lastRefund["amount"].ToString());

                charge.AmountInCents = refundInCents;
            }

            // Retrieve company by customer
            DomainCompany company = await _companyService.FindByCustomerAsync(charge.CustomerId);

            // updating balance
            var balanceRecord = new DomainBalanceHistory
            {
                Amount      = -charge.AmountInCents,
                Description = BillingMessages.ChargeRefunded,
                CompanyId   = company.Id
            };

            await _balanceService.AddAsync(balanceRecord);

            // Notify client about payment operation result
            try
            {
                await _notificationService.SendPaymentNotificationAsync(billingEvent, company, charge);
            }
            catch (Exception e)
            {
                Trace.TraceError("Failed to send payment notification e-mail to company {0}: {1}", company.Id, e);
            }
        }
        public async Task <HttpResponseMessage> Post(ClientPaymentModel model)
        {
            // Get company for user
            DomainCompany company = await _companyService.FindByUserAsync(UserId);

            var chargeCreateOptions = new CompanyChargeOptions
            {
                Id            = company.Id,
                AmountInCents = model.AmountInCents,
                Currency      = DomainCurrencies.Default,
                Description   = model.Description,
                TokenId       = model.TokenId
            };

            // Charging

            // balance will be updated after callback (webhook) from billing system
            await _companyService.ChargeAsync(chargeCreateOptions);

            HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created);

            return(response);
        }
        public async Task <ActionResult> ActivateClient(string code)
        {
            DomainPendingClient model;

            try
            {
                model = await _pendingClientService.GetAndDeleteAsync(code);
            }
            catch (NotFoundException)
            {
                return((ActionResult)RedirectToAction("Index", "Home"));
            }
            catch (Exception e)
            {
                Trace.TraceError("Failed to find client: {0}", e);
                return(new HttpStatusCodeResult(HttpStatusCode.InternalServerError));
            }

            // Add profile
            var user = new DomainUser
            {
                ApplicationName = AppName,
                Name            = model.ContactPerson,
                Country         = model.Country,
                UserAgent       = _productIdExtractor.Get(Request.UserAgent),
                Email           = model.Email,
                Roles           = new List <string> {
                    DomainRoles.Client
                }
            };

            try
            {
                user = await _userService.AddAsync(user);
            }
            catch (ConflictException)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.Conflict));
            }
            catch (Exception e)
            {
                Trace.TraceError("Failed to create user profile for {0}: {1}", model.Email, e);
                return(new HttpStatusCodeResult(HttpStatusCode.InternalServerError));
            }

            // Set user password
            try
            {
                await _passwordService.SetPasswordAsync(user.Id, model.Password, model.PasswordSalt);
            }
            catch (Exception e)
            {
                Trace.TraceError("Failed to set user {0} password: {1}", user.Id, e);
                return(new HttpStatusCodeResult(HttpStatusCode.InternalServerError));
            }

            // Add company
            var company = new DomainCompany
            {
                Email   = model.Email,
                Name    = model.CompanyName,
                Address = model.Address,
                ZipCode = model.ZipCode,
                Phone   = model.PhoneNumber,
                Country = model.Country,
                Ein     = model.Ein,
                Users   = new List <string> {
                    user.Id
                }
            };

            try
            {
                await _companyService.AddAsync(company);
            }
            catch (Exception e)
            {
                Trace.TraceError("Failed to create company for user {0}: {1}", user.Id, e);
                return(new HttpStatusCodeResult(HttpStatusCode.InternalServerError));
            }

            // Authenticate
            await _authenticationService.SetUserAsync(user, null, true);

            return(RedirectToRoute(RouteNames.ClientSubscriptions));
        }
Beispiel #14
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);
        }
Beispiel #15
0
        public void TestInaccauntableClicks()
        {
            // 1. Arrange

            // config
            _config.Setup(x => x.SubscriptionPlans)
            .Returns(
                () =>
                JObject.Parse(
                    "{ 0: { syncPeriodMs: 0, cyclePeriodMs: 0, clickRates: { 0: 0 } }, 1: { syncPeriodMs: 60000, cyclePeriodMs: 2592000000, clickRates: { 0: 0, 20: 10 } }, 2: { syncPeriodMs: 2592000000, cyclePeriodMs: 2592000000, clickRates: { 0: 0, 100000: 0.05, 1000000: 0.01 } }, 3: { syncPeriodMs: 2592000000, cyclePeriodMs: 2592000000, clickRates: { 0: 0, 100000: 0.05, 1000000: 0.01 } } }"));
            _config.Setup(x => x.BillingInvoiceItemDescriptionTemplate).Returns(() => "{0}");

            // stats service
            var stas = new List <TrackingStatEntity>
            {
                new TrackingStatEntity {
                    Date = new DateTime(2014, 6, 4, 14, 28, 35, 596)
                },
                new TrackingStatEntity {
                    Date = new DateTime(2014, 6, 4, 14, 28, 36, 995)
                },
                new TrackingStatEntity {
                    Date = new DateTime(2014, 6, 4, 14, 28, 41, 889)
                },
                new TrackingStatEntity {
                    Date = new DateTime(2014, 6, 4, 14, 28, 44, 249)
                },
                new TrackingStatEntity {
                    Date = new DateTime(2014, 6, 4, 14, 28, 48, 335)
                }
            };

            _statService.Setup(x => x.GetTotalAsync(It.IsAny <string>(), It.IsAny <DateTime?>(), It.IsAny <DateTime?>())).Returns <string, DateTime?, DateTime?>((i, f, t) =>
            {
                IQueryable <TrackingStatEntity> r = stas.AsQueryable();
                if (f.HasValue)
                {
                    r = r.Where(s => s.Date >= f.Value);
                }

                if (t.HasValue)
                {
                    r = r.Where(s => s.Date < t.Value);
                }

                return(Task.FromResult(r.LongCount()));
            });

            // company service
            var company = new DomainCompany
            {
                State         = ResourceState.Available,
                Subscriptions = new List <CompanySubscription>
                {
                    new CompanySubscription
                    {
                        State          = ResourceState.Available,
                        Type           = SubscriptionType.Basic,
                        Created        = new DateTime(2014, 6, 4, 14, 25, 14, 133),
                        LastSyncDate   = null,
                        LastCycleDate  = new DateTime(2014, 6, 4, 14, 27, 37, 939),
                        HasTrialClicks = false
                    }
                }
            };
            var companies = new List <DomainCompany> {
                company
            };

            _companyService.Setup(x => x.ListAsync()).Returns(() => Task.FromResult(companies.AsEnumerable()));

            // balance service
            var balance = new DomainBalanceHistory();

            _balanceService.Setup(x => x.AddAsync(It.IsAny <DomainBalanceHistory>())).Returns <DomainBalanceHistory>(b =>
            {
                balance = b;
                return(Task.FromResult(balance));
            });


            // 2. Act
            var syncDate = new DateTime(2014, 6, 4, 14, 28, 37, 939);

            _syncManager.SyncAsync(syncDate).Wait();


            // 3. Assert
            long actualCount = Int64.Parse(balance.Description);

            Assert.AreEqual(2, actualCount);
        }
        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));
        }
Beispiel #17
0
        public async Task SendPaymentNotificationAsync(DomainEvent billingEvent, DomainCompany company, DomainCharge charge)
        {
            if (billingEvent == null)
            {
                throw new ArgumentNullException("billingEvent");
            }

            if (company == null)
            {
                throw new ArgumentNullException("company");
            }

            if (charge == null)
            {
                throw new ArgumentNullException("charge");
            }

            if (!_settings.EmailNotifications)
            {
                return;
            }

            var email = new SendEmailDomain
            {
                Address     = _settings.EmailAddressAlerts,
                DisplayName = Emails.SenderDisplayName,
                Emails      = new List <string> {
                    company.Email
                }
            };

            switch (billingEvent.Type)
            {
            case EventType.ChargeFailed:
                email.Subject = Emails.SubjectPaymentFailed;
                email.Body    = string.Format(
                    PortalResources.PaymentFailed,
                    company.Name,
                    billingEvent.Id,
                    string.Format("{0} {1}", charge.AmountInCents * 0.01, charge.Currency),
                    charge.Created);
                break;

            case EventType.ChargeSucceeded:
                email.Subject = Emails.SubjectPaymentCompleted;
                email.Body    = string.Format(
                    PortalResources.PaymentCompleted,
                    company.Name,
                    billingEvent.Id,
                    string.Format("{0} {1}", charge.AmountInCents * 0.01, charge.Currency),
                    charge.Created);
                break;

            case EventType.ChargeRefunded:
                email.Subject = Emails.SubjectPaymentRefunded;
                email.Body    = string.Format(
                    PortalResources.PaymentRefunded,
                    company.Name,
                    billingEvent.Id,
                    string.Format("{0} {1}", charge.AmountInCents * 0.01, charge.Currency),
                    charge.Created);
                break;

            default:
                return;
            }


            // Send email on user registration
            await _emailSenderService.SendEmailAsync(email);
        }