/// <summary>
        /// Saves/updates the payment method for the member, at the remote gateway,
        /// updates in place, and returns, the ID/token
        /// </summary>
        /// <returns>The saved payment method ID/token</returns>
        /// <param name="paymentData"></param>
        private static string CollectPaymentMethod(LcPayment.InputPaymentMethod paymentData, int memberUserID)
        {
            // On emulation, discard other steps, just generate
            // a fake ID
            if (LcPayment.TESTING_EMULATEBRAINTREE)
            {
                paymentData.paymentMethodID = LcPayment.CreateFakePaymentMethodId();
                return(paymentData.paymentMethodID);
            }

            // Standard way
            var gateway = LcPayment.NewBraintreeGateway();

            // The input paymentID must be one generated by Braintree, reset any (malicious?) attempt
            // to provide a special temp ID generated by this method
            if (paymentData.IsTemporaryID())
            {
                paymentData.paymentMethodID = null;
            }

            // Find or create Customer on Braintree (for membership subscriptions, the member
            // is a customer of Loconomics).
            var client = LcPayment.GetOrCreateBraintreeCustomer(LcPayment.Membership.GetFeePaymentUserId(memberUserID));

            // Quick way for saved payment method that does not needs to be updated
            if (paymentData.IsSavedID())
            {
                // Just double check payment exists to avoid mistake/malicious attempts:
                if (!paymentData.ExistsOnVault())
                {
                    // Since we have not input data to save, we can only throw an error
                    // invalidSavedPaymentMethod
                    throw new ConstraintException("[[[Chosen payment method has expired]]]");
                }
            }
            else
            {
                // Creates or updates a payment method with the given data

                // Must we set an ID as temporary to prevent it appears as a saved payment method?
                //paymentData.paymentMethodID = LcPayment.TempSavedCardPrefix + ASP.LcHelpers.Channel + "_paymentPlan";

                // Save on Braintree secure Vault
                // It updates the paymentMethodID if a new one was generated
                var saveCardError = paymentData.SaveInVault(client.Id);
                if (!String.IsNullOrEmpty(saveCardError))
                {
                    // paymentDataError
                    throw new ConstraintException(saveCardError);
                }
            }

            return(paymentData.paymentMethodID);
        }
        public static UserPaymentPlan CreateSubscription(
            int userID,
            SubscriptionPlan plan,
            LcPayment.InputPaymentMethod paymentMethod)
        {
            // Prepare payment method (in the remote gateway), get its ID
            var paymentMethodToken = CollectPaymentMethod(paymentMethod, userID);

            // Prepare initial object
            var userPlan = new UserPaymentPlan()
            {
                userID              = userID,
                paymentPlan         = plan,
                subscriptionEndDate = null
            };

            // Create subscription at gateway and set details
            // Wrapped in a try-catch to implement a transaction-like operation:
            // if something fail after succesfully create the Braintree subscription, like not being
            // able to save details on database, we need to 'rollback' the subscription, asking for removal
            // to Braintree
            string generatedSubscriptionId = null;
            var    paymentPlan             = new LcPayment.Membership();

            try
            {
                if (LcPayment.TESTING_EMULATEBRAINTREE)
                {
                    userPlan.subscriptionID             = LcPayment.CreateFakeSubscriptionId();
                    userPlan.paymentPlanLastChangedDate = DateTimeOffset.Now;
                    userPlan.nextPaymentDueDate         = DateTimeOffset.Now.Add(new TimeSpan(365, 0, 0, 0));
                    userPlan.nextPaymentAmount          = 99;
                    userPlan.firstBillingDate           = DateTimeOffset.Now;
                    userPlan.planStatus  = "ACTIVE";
                    userPlan.daysPastDue = 0;
                }
                else
                {
                    // Start creating the subscription at the payment gateway
                    var trialEndDate = GetUserTrialEndDate(userID);

                    // Create the subscription at the payment gateway
                    // It returns the subscription object with a correct ID on success, otherwise an exception is thrown
                    var subscription = paymentPlan.CreateSubscription(plan, paymentMethodToken, trialEndDate);
                    generatedSubscriptionId             = subscription.Id;
                    userPlan.subscriptionID             = subscription.Id;
                    userPlan.paymentPlanLastChangedDate = subscription.UpdatedAt.Value;
                    userPlan.nextPaymentDueDate         = subscription.NextBillingDate;
                    userPlan.nextPaymentAmount          = subscription.NextBillAmount;
                    userPlan.firstBillingDate           = subscription.FirstBillingDate.Value;
                    userPlan.planStatus  = subscription.Status.ToString();
                    userPlan.daysPastDue = subscription.DaysPastDue ?? 0;
                }

                // Fill payment method info
                var info = LcPayment.PaymentMethodInfo.Get(paymentMethodToken);
                userPlan.paymentExpiryDate  = info.ExpirationDate;
                userPlan.paymentMethodToken = paymentMethodToken;
                userPlan.paymentMethod      = info.Description;

                // Persist subscription on database
                Set(userPlan);
            }
            catch (Exception ex)
            {
                // Rollback
                if (generatedSubscriptionId != null)
                {
                    // Rollback subscription at Payment Gateway
                    paymentPlan.CancelSubscription(generatedSubscriptionId);
                }

                // The exception needs to be communicated anyway, so re-throw
                throw new Exception("[[[Failed subscription]]]", ex);
            }

            return(userPlan);
        }