/// <summary>
        /// Purchases one or more partner offers.
        /// </summary>
        /// <param name="order">The order to execute.</param>
        /// <returns>A transaction result which summarizes its outcome.</returns>
        public async Task <TransactionResult> PurchaseAsync(OrderViewModel order)
        {
            // use the normalizer to validate the order.
            OrderNormalizer orderNormalizer = new OrderNormalizer(this.ApplicationDomain, order);

            order = await orderNormalizer.NormalizePurchaseSubscriptionOrderAsync();

            // build the purchase line items.
            List <PurchaseLineItem> purchaseLineItems = new List <PurchaseLineItem>();

            foreach (var orderItem in order.Subscriptions)
            {
                string offerId  = orderItem.OfferId;
                int    quantity = orderItem.Quantity;

                purchaseLineItems.Add(new PurchaseLineItem(offerId, quantity));
            }

            // associate line items in order to partner offers.
            var lineItemsWithOffers = await this.AssociateWithPartnerOffersAsync(purchaseLineItems);

            ICollection <IBusinessTransaction> subTransactions = new List <IBusinessTransaction>();

            // prepare payment authorization
            var paymentAuthorization = new AuthorizePayment(this.PaymentGateway);

            subTransactions.Add(paymentAuthorization);

            // build the Partner Center order and pass it to the place order transaction
            Order partnerCenterPurchaseOrder = this.BuildPartnerCenterOrder(lineItemsWithOffers);

            var placeOrder = new PlaceOrder(
                this.ApplicationDomain.PartnerCenterClient.Customers.ById(this.CustomerId),
                partnerCenterPurchaseOrder);

            subTransactions.Add(placeOrder);

            // configure a transaction to save the new resulting subscriptions and purchases into persistence
            var persistSubscriptionsAndPurchases = new PersistNewlyPurchasedSubscriptions(
                this.CustomerId,
                this.ApplicationDomain.CustomerSubscriptionsRepository,
                this.ApplicationDomain.CustomerPurchasesRepository,
                () => new Tuple <Order, IEnumerable <PurchaseLineItemWithOffer> >(placeOrder.Result, lineItemsWithOffers));

            subTransactions.Add(persistSubscriptionsAndPurchases);

            // configure a capture payment transaction and let it read the auth code from the payment authorization output
            var capturePayment = new CapturePayment(this.PaymentGateway, () => paymentAuthorization.Result);

            subTransactions.Add(capturePayment);

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

            return(new TransactionResult(persistSubscriptionsAndPurchases.Result, DateTime.UtcNow));
        }
        /// <summary>
        /// Purchases additional seats for an existing subscription the customer has already bought.
        /// </summary>
        /// <param name="order">The order to execute.</param>
        /// <returns>A transaction result which summarizes its outcome.</returns>
        public async Task <TransactionResult> PurchaseAdditionalSeatsAsync(OrderViewModel order)
        {
            // use the normalizer to validate the order.
            OrderNormalizer orderNormalizer = new OrderNormalizer(this.ApplicationDomain, order);

            order = await orderNormalizer.NormalizePurchaseAdditionalSeatsOrderAsync();

            List <OrderSubscriptionItemViewModel> orderSubscriptions = order.Subscriptions.ToList();
            string  subscriptionId     = orderSubscriptions.First().SubscriptionId;
            int     seatsToPurchase    = orderSubscriptions.First().Quantity;
            decimal proratedSeatCharge = orderSubscriptions.First().SeatPrice;
            string  partnerOfferId     = orderSubscriptions.First().PartnerOfferId;

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

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

            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,
                partnerOfferId,
                seatsToPurchase,
                proratedSeatCharge,
                seatsToPurchase * proratedSeatCharge);

            return(new TransactionResult(
                       new TransactionResultLineItem[] { additionalSeatsPurchaseResult },
                       rightNow));
        }
        /// <summary>
        /// Renews an existing subscription for a customer.
        /// </summary>
        /// <param name="order">The order to execute.</param>
        /// <returns>A transaction result which summarizes its outcome.</returns>
        public async Task <TransactionResult> RenewSubscriptionAsync(OrderViewModel order)
        {
            // use the normalizer to validate the order.
            OrderNormalizer orderNormalizer = new OrderNormalizer(this.ApplicationDomain, order);

            order = await orderNormalizer.NormalizeRenewSubscriptionOrderAsync();

            List <OrderSubscriptionItemViewModel> orderSubscriptions = order.Subscriptions.ToList();
            string   subscriptionId         = orderSubscriptions.First().SubscriptionId;
            string   partnerOfferId         = orderSubscriptions.First().PartnerOfferId;
            decimal  partnerOfferPrice      = orderSubscriptions.First().SeatPrice;
            DateTime subscriptionExpiryDate = orderSubscriptions.First().SubscriptionExpiryDate;
            int      quantity    = orderSubscriptions.First().Quantity;
            decimal  totalCharge = Math.Round(quantity * partnerOfferPrice, Resources.Culture.NumberFormat.CurrencyDecimalDigits);

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

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

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

            subTransactions.Add(paymentAuthorization);

            // add a renew subscription transaction to the pipeline
            subTransactions.Add(new RenewSubscription(
                                    subscriptionOperations,
                                    partnerCenterSubscription));

            DateTime rightNow = DateTime.UtcNow;

            // record the renewal in our purchase store
            subTransactions.Add(new RecordPurchase(
                                    this.ApplicationDomain.CustomerPurchasesRepository,
                                    new CustomerPurchaseEntity(CommerceOperationType.Renewal, Guid.NewGuid().ToString(), this.CustomerId, subscriptionId, partnerCenterSubscription.Quantity, partnerOfferPrice, rightNow)));

            // extend the expiry date by one year
            subTransactions.Add(new UpdatePersistedSubscription(
                                    this.ApplicationDomain.CustomerSubscriptionsRepository,
                                    new CustomerSubscriptionEntity(this.CustomerId, subscriptionId, partnerOfferId, subscriptionExpiryDate.AddYears(1))));

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

            // run the pipeline
            await CommerceOperations.RunAggregatedTransaction(subTransactions);

            var renewSubscriptionResult = new TransactionResultLineItem(
                subscriptionId,
                partnerOfferId,
                partnerCenterSubscription.Quantity,
                partnerOfferPrice,
                totalCharge);

            return(new TransactionResult(
                       new TransactionResultLineItem[] { renewSubscriptionResult },
                       rightNow));
        }