private AuthorizeNet.Order CreatePaymentOrder(IOrderGroup orderGroup, TransactionData transactionData, ITokenizedPayment payment, decimal orderAmount)
        {
            var customer = _authorizeTokenExService.CreateCustomer(transactionData.invoiceNum, payment.BillingAddress);

            var address = Utilities.ToAuthorizeNetAddress(payment.BillingAddress);

            customer.BillingAddress = address;
            _authorizeTokenExService.UpdateCustomer(customer);

            var shippingAddress = orderGroup.Forms.First().Shipments.First().ShippingAddress;

            if (shippingAddress == null)
            {
                throw new PaymentException(PaymentException.ErrorType.ConfigurationError, "", "Shipping address was not specified.");
            }

            var paymentProfileId  = AddCreditCard(orderGroup, customer.ProfileID, payment.Token, payment.ExpirationMonth, payment.ExpirationYear);
            var shippingAddressId = AddShippingAddress(customer.ProfileID, Utilities.ToAuthorizeNetAddress(shippingAddress));

            var currency         = orderGroup.Currency;
            var market           = _marketService.GetMarket(orderGroup.MarketId);
            var merchantCurrency = _authorizeTokenExService.GetMerchantCurrency();
            var shippingTotal    = Utilities.ConvertMoney(orderGroup.GetShippingTotal(_orderGroupCalculator), merchantCurrency);
            var salesTaxTotal    = Utilities.ConvertMoney(
                new Money(orderGroup.Forms.Sum(form => form.Shipments.Sum(shipment =>
                                                                          _shippingCalculator.GetSalesTax(shipment, market, currency).Amount)), currency).Round(),
                merchantCurrency);
            var taxTotal = Utilities.ConvertMoney(orderGroup.GetTaxTotal(_orderGroupCalculator), merchantCurrency);

            var order = new AuthorizeNet.Order(customer.ProfileID, paymentProfileId, shippingAddressId)
            {
                Amount         = orderAmount - taxTotal,
                SalesTaxAmount = salesTaxTotal,
                PONumber       = transactionData.purchaseOrderNum,
                InvoiceNumber  = transactionData.invoiceNum,
                ShippingAmount = shippingTotal + (orderGroup.PricesIncludeTax ? 0 : taxTotal - salesTaxTotal)
            };

            var orderedLineItems    = orderGroup.GetAllLineItems().OrderBy(x => x.Quantity).ToList();
            var largestQuantityItem = orderedLineItems.LastOrDefault();

            var lineItemsAmount             = 0m;
            var amountWithoutLargestQtyItem = 0m;
            var settleSubTotalExclTax       = orderAmount - order.ShippingAmount - order.SalesTaxAmount;
            var isFirstLineItem             = true;
            var factor        = (decimal)Math.Pow(10, DecimalDigit);
            var roundingDelta = 1 / factor;
            var itemPriceExcludingTaxMapping = GetPriceExcludingTax(orderGroup);

            foreach (var lineItem in orderedLineItems)
            {
                var itemCode     = lineItem.Code;
                var itemQuantity = Convert.ToInt32(lineItem.Quantity);
                var displayName  = string.IsNullOrEmpty(lineItem.DisplayName) ? itemCode : lineItem.DisplayName;
                var description  = (lineItem as Mediachase.Commerce.Orders.LineItem)?.Description ?? string.Empty;

                if (lineItem.IsGift)
                {
                    itemCode    = $"Gift: {itemCode}";
                    displayName = $"Gift: {displayName}";
                    description = $"Gift: {description}";
                }

                itemCode    = Utilities.StripPreviewText(itemCode, 31);
                displayName = Utilities.StripPreviewText(displayName, 31);
                description = Utilities.StripPreviewText(description, 255);

                // Calculate unit price.
                var unitPrice = Utilities.ConvertMoney(
                    new Money(currency.Round(itemPriceExcludingTaxMapping[lineItem.LineItemId] / lineItem.Quantity, DecimalDigit), currency),
                    merchantCurrency);

                lineItemsAmount += unitPrice * lineItem.Quantity;

                if (lineItem.LineItemId != largestQuantityItem.LineItemId)
                {
                    amountWithoutLargestQtyItem = lineItemsAmount;
                }

                // In case we have Rounding Differences between rounding Total Amount (orderAmount - amount requested for settlement)
                // and rounding each lineItem unit price (amount authorized), need to recalculate unit price of item has largest quantity.
                if (lineItem.LineItemId == largestQuantityItem.LineItemId && lineItemsAmount < settleSubTotalExclTax)
                {
                    // Choose largestQuantityItem to make the unitPrice difference before and after recalculate smallest.
                    unitPrice = (settleSubTotalExclTax - amountWithoutLargestQtyItem) / largestQuantityItem.Quantity;

                    // Round up to make sure amount authorized >= requested for settlement amount.
                    unitPrice = merchantCurrency.Round(unitPrice + roundingDelta, DecimalDigit);
                }

                if (!isFirstLineItem)
                {
                    // If the lineitem price to add to the total exceed the Amount chosen
                    // for the payment. The line item price is adjusted, since it will be covered by other payment method.
                    if (unitPrice * lineItem.Quantity + order.Total > order.Amount)
                    {
                        var lineItemPrice = orderAmount - order.Total;

                        // If this is the last item, then we need to round up to make sure amount authorized >= requested for settlement amount.
                        if (lineItem.LineItemId == largestQuantityItem.LineItemId)
                        {
                            unitPrice = merchantCurrency.Round(lineItemPrice / lineItem.Quantity + roundingDelta, DecimalDigit);
                        }
                        else
                        {
                            unitPrice = merchantCurrency.Round(lineItemPrice / lineItem.Quantity, DecimalDigit);
                        }
                    }
                }

                // Note that order total calculated from authorized.net order constructor is recalculated by adding line items.
                order.AddLineItem(itemCode, displayName, description, itemQuantity, unitPrice, null);

                // We need to handle the case of first line item after being added to the order
                // since order total is recalculated when is added to the order.
                if (isFirstLineItem)
                {
                    // If with the first line item the amount chosen for the payment is exceeding
                    // the price added to the line item then should be the amount chosen.
                    if (order.Total > orderAmount)
                    {
                        // In case the order has only one line item, then we need to round up to make sure amount authorized >= requested for settlement amount.
                        if (lineItem.LineItemId == largestQuantityItem.LineItemId)
                        {
                            unitPrice = merchantCurrency.Round(settleSubTotalExclTax / lineItem.Quantity + roundingDelta, DecimalDigit);
                        }
                        else
                        {
                            unitPrice = merchantCurrency.Round(settleSubTotalExclTax / lineItem.Quantity, DecimalDigit);
                        }

                        order.RemoveLineItem(itemCode);
                        order.AddLineItem(itemCode, displayName, description, itemQuantity, unitPrice, null);
                    }
                }
                isFirstLineItem = false;
            }
            payment.Properties[AuthorizeTokenExGateway.ProviderProfileIdPropertyName] = customer.ProfileID;

            return(order);
        }