/// <summary>
        /// Purchases additional seats for an existing subscription the customer has already bought.
        /// </summary>
        /// <param name="subscriptionId">The ID of the subscription for which to increase its quantity.</param>
        /// <param name="seatsToPurchase">The number of new seats to purchase on top of the existing ones.</param>
        /// <returns>A transaction result which summarizes its outcome.</returns>
        public async Task <TransactionResult> PurchaseAdditionalSeatsAsync(string subscriptionId, int seatsToPurchase)
        {
            // validate inputs
            subscriptionId.AssertNotEmpty("subscriptionId");
            seatsToPurchase.AssertPositive("seatsToPurchase");

            // we will add up the transactions here
            ICollection <IBusinessTransaction> subTransactions = new List <IBusinessTransaction>();

            // determine the prorated seat charge
            var subscriptionToAugment = await this.GetSubscriptionAsync(subscriptionId);

            var partnerOffer = await this.ApplicationDomain.OffersRepository.RetrieveAsync(subscriptionToAugment.PartnerOfferId);

            // if subscription expiry date.Date is less than today's UTC date then subcription has expired.
            if (subscriptionToAugment.ExpiryDate.Date < DateTime.UtcNow.Date)
            {
                // this subscription has already expired, don't permit adding seats until the subscription is renewed
                throw new PartnerDomainException(ErrorCode.SubscriptionExpired);
            }

            decimal proratedSeatCharge = Math.Round(CommerceOperations.CalculateProratedSeatCharge(subscriptionToAugment.ExpiryDate, partnerOffer.Price), 2);
            decimal totalCharge        = Math.Round(proratedSeatCharge * seatsToPurchase, 2);

            // configure a transaction to charge the payment gateway with the prorated rate
            var paymentAuthorization = new AuthorizePayment(this.PaymentGateway, totalCharge);

            subTransactions.Add(paymentAuthorization);

            // configure a purchase additional seats transaction with the requested seats to purchase
            subTransactions.Add(new PurchaseExtraSeats(
                                    this.ApplicationDomain.PartnerCenterClient.Customers.ById(this.CustomerId).Subscriptions.ById(subscriptionId),
                                    seatsToPurchase));

            DateTime rightNow = DateTime.UtcNow;

            // record the purchase in our purchase store
            subTransactions.Add(new RecordPurchase(
                                    this.ApplicationDomain.CustomerPurchasesRepository,
                                    new CustomerPurchaseEntity(CommerceOperationType.AdditionalSeatsPurchase, Guid.NewGuid().ToString(), this.CustomerId, subscriptionId, seatsToPurchase, proratedSeatCharge, rightNow)));

            // add a capture payment to the transaction pipeline
            subTransactions.Add(new CapturePayment(this.PaymentGateway, () => paymentAuthorization.Result));

            // build an aggregated transaction from the previous steps and execute it as a whole
            await CommerceOperations.RunAggregatedTransaction(subTransactions);

            var additionalSeatsPurchaseResult = new TransactionResultLineItem(
                subscriptionId,
                subscriptionToAugment.PartnerOfferId,
                seatsToPurchase,
                proratedSeatCharge,
                seatsToPurchase * proratedSeatCharge);

            return(new TransactionResult(
                       totalCharge,
                       new TransactionResultLineItem[] { additionalSeatsPurchaseResult },
                       rightNow));
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Normalizes an order to add seats to a subscription.
        /// </summary>
        /// <returns>Normalized order.</returns>
        public async Task <OrderViewModel> NormalizePurchaseAdditionalSeatsOrderAsync()
        {
            OrderViewModel order = this.Order;

            order.CustomerId.AssertNotEmpty(nameof(order.CustomerId));
            if (order.OperationType != CommerceOperationType.AdditionalSeatsPurchase)
            {
                throw new PartnerDomainException(ErrorCode.InvalidInput, Resources.InvalidOperationForOrderMessage).AddDetail("Field", "OperationType");
            }

            // create result order object prefilling it with operation type & customer id.
            OrderViewModel orderResult = new OrderViewModel()
            {
                CustomerId    = order.CustomerId,
                OrderId       = order.OrderId,
                OperationType = order.OperationType
            };

            order.Subscriptions.AssertNotNull(nameof(order.Subscriptions));
            List <OrderSubscriptionItemViewModel> orderSubscriptions = order.Subscriptions.ToList();

            if (!(orderSubscriptions.Count == 1))
            {
                throw new PartnerDomainException(ErrorCode.InvalidInput).AddDetail("ErrorMessage", Resources.MoreThanOneSubscriptionUpdateErrorMessage);
            }

            string subscriptionId  = orderSubscriptions.First().SubscriptionId;
            int    seatsToPurchase = orderSubscriptions.First().Quantity;

            subscriptionId.AssertNotEmpty(nameof(subscriptionId)); // is Required for the commerce operation.
            seatsToPurchase.AssertPositive("seatsToPurchase");

            // grab the customer subscription from our store
            var subscriptionToAugment = await this.GetSubscriptionAsync(subscriptionId, order.CustomerId);

            // retrieve the partner offer this subscription relates to, we need to know the current price
            var partnerOffer = await ApplicationDomain.Instance.OffersRepository.RetrieveAsync(subscriptionToAugment.PartnerOfferId);

            if (partnerOffer.IsInactive)
            {
                // renewing deleted offers is prohibited
                throw new PartnerDomainException(ErrorCode.PurchaseDeletedOfferNotAllowed).AddDetail("Id", partnerOffer.Id);
            }

            // retrieve the subscription from Partner Center
            var subscriptionOperations    = ApplicationDomain.Instance.PartnerCenterClient.Customers.ById(order.CustomerId).Subscriptions.ById(subscriptionId);
            var partnerCenterSubscription = await subscriptionOperations.GetAsync();

            // if subscription expiry date.Date is less than today's UTC date then subcription has expired.
            if (subscriptionToAugment.ExpiryDate.Date < DateTime.UtcNow.Date)
            {
                // this subscription has already expired, don't permit adding seats until the subscription is renewed
                throw new PartnerDomainException(ErrorCode.SubscriptionExpired);
            }

            decimal proratedSeatCharge = Math.Round(CommerceOperations.CalculateProratedSeatCharge(subscriptionToAugment.ExpiryDate, partnerOffer.Price), Resources.Culture.NumberFormat.CurrencyDecimalDigits);
            decimal totalCharge        = Math.Round(proratedSeatCharge * seatsToPurchase, Resources.Culture.NumberFormat.CurrencyDecimalDigits);

            List <OrderSubscriptionItemViewModel> resultOrderSubscriptions = new List <OrderSubscriptionItemViewModel>();

            resultOrderSubscriptions.Add(new OrderSubscriptionItemViewModel()
            {
                OfferId                = subscriptionId,
                SubscriptionId         = subscriptionId,
                PartnerOfferId         = subscriptionToAugment.PartnerOfferId,
                SubscriptionExpiryDate = subscriptionToAugment.ExpiryDate,
                Quantity               = seatsToPurchase,
                SeatPrice              = proratedSeatCharge,
                SubscriptionName       = partnerOffer.Title
            });

            orderResult.Subscriptions = resultOrderSubscriptions;
            return(await Task.FromResult(orderResult));
        }