/// <summary>
        /// The Update function won't accept a change to some values - specifically the billing interval. This creates a request
        /// that the API can understand for updates only
        /// </summary>
        /// <returns></returns>
        public ARBSubscriptionType ToUpdateableAPI() {

            var sub = new ARBSubscriptionType();
            sub.name = this.SubscriptionName;

            if (!String.IsNullOrEmpty(this.CardNumber) && (this.CardNumber.Trim().Length > 0))
            {
                DateTime dt;
                if (!CommonFunctions.ParseDateTime(this.CardExpirationYear, this.CardExpirationMonth, 1, out dt))
                {
                    throw new InvalidOperationException("Need a valid CardExpirationMonth and CardExpirationYear to set up this subscription");
                }

                var creditCard = new creditCardType();
                creditCard.cardNumber = this.CardNumber;
                creditCard.expirationDate = dt.ToString("yyyy-MM");//string.Format("{0}-{1}", this.CardExpirationYear, this.CardExpirationMonth);  // required format for API is YYYY-MM
                sub.payment = new paymentType();
                sub.payment.Item = creditCard;
            }

            if ((this.eCheckBankAccount != null) && !String.IsNullOrEmpty(this.eCheckBankAccount.accountNumber) &&
                 (this.eCheckBankAccount.accountNumber.Trim().Length >0))
            {
                var eCheck = new bankAccountType()
                {
                    accountTypeSpecified = eCheckBankAccount.accountTypeSpecified,
                    accountType = (bankAccountTypeEnum)Enum.Parse(typeof(bankAccountTypeEnum), eCheckBankAccount.accountType.ToString(), true),
                    routingNumber = eCheckBankAccount.routingNumber,
                    accountNumber = eCheckBankAccount.accountNumber,
                    nameOnAccount = eCheckBankAccount.nameOnAccount,
                    echeckTypeSpecified = eCheckBankAccount.echeckTypeSpecified,
                    echeckType = (echeckTypeEnum)Enum.Parse(typeof(echeckTypeEnum), eCheckBankAccount.echeckType.ToString(), true),
                    bankName = eCheckBankAccount.bankName,
                    checkNumber = eCheckBankAccount.checkNumber
                };
                sub.payment = new paymentType { Item = eCheck };
            }

            if (this.BillingAddress != null)
                sub.billTo = this.BillingAddress.ToAPINameAddressType();
            if (this.ShippingAddress != null)
                sub.shipTo = this.ShippingAddress.ToAPINameAddressType();

            sub.paymentSchedule = new paymentScheduleType();
            sub.paymentSchedule.totalOccurrences = this.BillingCycles;
            sub.paymentSchedule.totalOccurrencesSpecified = true;

            sub.amount = this.Amount;
            sub.amountSpecified = true;

            sub.customer = new customerType();
            sub.customer.email = this.CustomerEmail;
            sub.customer.id = this.CustomerID;

            sub.order = new orderType();
            sub.order.description = this.Description;
            sub.order.invoiceNumber = this.Invoice;
                                                        
            return sub;

        }
        /// <summary>
        /// This is mostly for internal processing needs - it takes the SubscriptionRequest and turns it into something the Gateway can serialize.
        /// </summary>
        /// <returns></returns>
        public ARBSubscriptionType ToAPI(){

            var sub = new ARBSubscriptionType();
            sub.name = this.SubscriptionName;

            bool isCard = true;
            StringBuilder sbError = new StringBuilder("");
            bool bError = false;
            if (String.IsNullOrEmpty(this.CardNumber) || (this.CardNumber.Trim().Length == 0))
            {
                if ((null == this.eCheckBankAccount) || String.IsNullOrEmpty(this.eCheckBankAccount.accountNumber) ||
                    (this.eCheckBankAccount.accountNumber.Trim().Length == 0))
                {
                    sbError.Append("Need a credit card number or a bank account number to set up this subscription");
                    bError = true;
                }
                else
                {
                    isCard = false;
                }
            }

            DateTime dt = new DateTime();
            if ( isCard && !CommonFunctions.ParseDateTime(this.CardExpirationYear, this.CardExpirationMonth, 1, out dt))
            {
                sbError.Append("Need a valid CardExpirationMonth and CardExpirationYear to set up this subscription");
                bError = true;
            }

            if (bError)
            {
                throw new InvalidOperationException(sbError.ToString());
            }

            if (isCard)
            {
                var creditCard = new creditCardType();
                creditCard.cardNumber = this.CardNumber;
                creditCard.expirationDate = dt.ToString("yyyy-MM"); // required format for API is YYYY-MM
                sub.payment = new paymentType();
                sub.payment.Item = creditCard;
            }
            else
            {
                var eCheck = new bankAccountType()
                    {
                        accountTypeSpecified = eCheckBankAccount.accountTypeSpecified,
                        accountType = (bankAccountTypeEnum)Enum.Parse(typeof(bankAccountTypeEnum), eCheckBankAccount.accountType.ToString(), true),
                        routingNumber = eCheckBankAccount.routingNumber,
                        accountNumber = eCheckBankAccount.accountNumber,
                        nameOnAccount = eCheckBankAccount.nameOnAccount,
                        echeckTypeSpecified = eCheckBankAccount.echeckTypeSpecified,
                        echeckType = (echeckTypeEnum)Enum.Parse(typeof(echeckTypeEnum), eCheckBankAccount.echeckType.ToString(), true),
                        bankName = eCheckBankAccount.bankName,
                        checkNumber = eCheckBankAccount.checkNumber
                    };
                sub.payment = new paymentType {Item = eCheck};
            }

            if(this.BillingAddress!=null)
                sub.billTo = this.BillingAddress.ToAPINameAddressType();
            if (this.ShippingAddress != null)
                sub.shipTo = this.ShippingAddress.ToAPINameAddressType();

            sub.paymentSchedule = new paymentScheduleType();
            sub.paymentSchedule.startDate = this.StartsOn;
            sub.paymentSchedule.startDateSpecified = true;

            sub.paymentSchedule.totalOccurrences = this.BillingCycles;
            sub.paymentSchedule.totalOccurrencesSpecified = true;

            // free 1 month trial
            if (this.TrialBillingCycles > 0) {
                sub.paymentSchedule.trialOccurrences = this.TrialBillingCycles;
                sub.paymentSchedule.trialOccurrencesSpecified = true;
            }

            if (this.TrialAmount > 0) {
                sub.trialAmount = this.TrialAmount;
                sub.trialAmountSpecified = true;
            }

            sub.amount = this.Amount;
            sub.amountSpecified = true;

            sub.paymentSchedule.interval = new paymentScheduleTypeInterval();
            sub.paymentSchedule.interval.length = this.BillingInterval;
            
            if (this.BillingIntervalUnits == BillingIntervalUnits.Months) {
                sub.paymentSchedule.interval.unit = ARBSubscriptionUnitEnum.months;
            } else {
                sub.paymentSchedule.interval.unit = ARBSubscriptionUnitEnum.days;
            }
            sub.customer = new customerType();
            sub.customer.email = this.CustomerEmail;

            sub.order = new orderType();
            sub.order.description = this.Description;
            sub.order.invoiceNumber = this.Invoice;

            sub.customer.id = this.CustomerID;

            return sub;

        }
        /// <summary>
        /// This is mostly for internal processing needs - it takes the SubscriptionRequest and turns it into something the Gateway can serialize.
        /// </summary>
        /// <returns></returns>
        public ARBSubscriptionType ToAPI()
        {
            var sub = new ARBSubscriptionType();
            sub.name = this.SubscriptionName;

            if (!String.IsNullOrEmpty(this.CardNumber)) {
                var creditCard = new creditCardType();
                creditCard.cardNumber = this.CardNumber;
                creditCard.expirationDate = string.Format("{0}-{1}", this.CardExpirationYear, this.CardExpirationMonth);  // required format for API is YYYY-MM
                sub.payment = new paymentType();
                sub.payment.Item = creditCard;
            } else {
                throw new InvalidOperationException("Need a credit card number to set up this subscription");
            }

            if(this.BillingAddress!=null)
                sub.billTo = this.BillingAddress.ToAPINameAddressType();
            if (this.ShippingAddress != null)
                sub.shipTo = this.ShippingAddress.ToAPINameAddressType();

            sub.paymentSchedule = new paymentScheduleType();
            sub.paymentSchedule.startDate = this.StartsOn;
            sub.paymentSchedule.startDateSpecified = true;

            sub.paymentSchedule.totalOccurrences = this.BillingCycles;
            sub.paymentSchedule.totalOccurrencesSpecified = true;

            // free 1 month trial
            if (this.TrialBillingCycles > 0) {
                sub.paymentSchedule.trialOccurrences = this.TrialBillingCycles;
                sub.paymentSchedule.trialOccurrencesSpecified = true;
            }

            if (this.TrialAmount > 0) {
                sub.trialAmount = this.TrialAmount;
                sub.trialAmountSpecified = true;
            }

            sub.amount = this.Amount;
            sub.amountSpecified = true;

            sub.paymentSchedule.interval = new paymentScheduleTypeInterval();
            sub.paymentSchedule.interval.length = this.BillingInterval;

            if (this.BillingIntervalUnits == BillingIntervalUnits.Months) {
                sub.paymentSchedule.interval.unit = ARBSubscriptionUnitEnum.months;
            } else {
                sub.paymentSchedule.interval.unit = ARBSubscriptionUnitEnum.days;
            }
            sub.customer = new customerType();
            sub.customer.email = this.CustomerEmail;

            sub.order = new orderType();
            sub.order.description = this.Description;
            sub.order.invoiceNumber = this.Invoice;

            sub.customer.id = this.CustomerID;

            return sub;
        }
        /// <summary>
        /// The Update function won't accept a change to some values - specifically the billing interval. This creates a request
        /// that the API can understand for updates only
        /// </summary>
        /// <returns></returns>
        public ARBSubscriptionType ToUpdateableAPI()
        {
            var sub = new ARBSubscriptionType();
            sub.name = this.SubscriptionName;

            if (!String.IsNullOrEmpty(this.CardNumber)) {
                var creditCard = new creditCardType();
                creditCard.cardNumber = this.CardNumber;
                creditCard.expirationDate = string.Format("{0}-{1}", this.CardExpirationYear, this.CardExpirationMonth);  // required format for API is YYYY-MM
                sub.payment = new paymentType();
                sub.payment.Item = creditCard;
            } else {
                throw new InvalidOperationException("Need a credit card number to set up this subscription");
            }

            if (this.BillingAddress != null)
                sub.billTo = this.BillingAddress.ToAPINameAddressType();
            if (this.ShippingAddress != null)
                sub.shipTo = this.ShippingAddress.ToAPINameAddressType();

            sub.paymentSchedule = new paymentScheduleType();
            sub.paymentSchedule.totalOccurrences = this.BillingCycles;
            sub.paymentSchedule.totalOccurrencesSpecified = true;

            sub.amount = this.Amount;
            sub.amountSpecified = true;

            sub.customer = new customerType();
            sub.customer.email = this.CustomerEmail;
            sub.customer.id = this.CustomerID;

            return sub;
        }
        public static AuthorizeNet.APICore.ARBSubscriptionType MapRecurringPaymentDataToSubscription(this IRecurringPaymentData request)
        {
            var result = new AuthorizeNet.APICore.ARBSubscriptionType();
            var paymentScheduleType = new AuthorizeNet.APICore.paymentScheduleType();
            var paymentScheduleTypeInterval = new AuthorizeNet.APICore.paymentScheduleTypeInterval();
            paymentScheduleTypeInterval.length = (short)request.Intervals;
            paymentScheduleTypeInterval.unit = request.RecurringPaymentInterval == RecurringPaymentIntervals.Days ? AuthorizeNet.APICore.ARBSubscriptionUnitEnum.days : AuthorizeNet.APICore.ARBSubscriptionUnitEnum.months;
                   
            paymentScheduleType.interval = paymentScheduleTypeInterval;
            paymentScheduleType.startDateSpecified = request.StartDate.HasValue;
            if (request.StartDate.HasValue)
                paymentScheduleType.startDate = request.StartDate.Value;
            //Defaults to 9999 no end
            paymentScheduleType.totalOccurrences = 9999;
            paymentScheduleType.totalOccurrencesSpecified = true;
            if(request.Intervals.HasValue)
                paymentScheduleType.totalOccurrences= (short)request.Intervals.Value;
            paymentScheduleType.trialOccurrencesSpecified = request.TrialIntervals.HasValue;
            if (request.TrialIntervals.HasValue)
            {
                paymentScheduleType.trialOccurrences = (short)request.TrialIntervals;
                result.trialAmountSpecified = request.TrialIntervalPrice.HasValue;
                if (request.TrialIntervalPrice.HasValue)
                    result.trialAmount = request.TrialIntervalPrice.Value;
            }
            result.paymentSchedule = paymentScheduleType;
            result.payment = new AuthorizeNet.APICore.paymentType();
            result.payment.Item = request.CardData.MapIPaymentCardDataToCreditCardType();
            result.customer = request.Customer.MapCustomerDataToCustomerType();
            result.billTo = request.Customer.MapCustomerDataToNameAddressType();
            result.amountSpecified = request.Transaction.Amount > 0;
            if (request.Transaction.Amount>0)
                result.amount = request.Transaction.Amount;

            return result;
            

            
        }