public void SignUpUser(ApplicationUser user, string emailTemplate, string subRole, string unSubRole,
                               string shareTemplate, string authCode = null)
        {
            Debug.Assert(null != user);
            Debug.Assert(!string.IsNullOrEmpty(emailTemplate));
            Debug.Assert(!string.IsNullOrEmpty(subRole));
            Debug.Assert(!string.IsNullOrEmpty(unSubRole));
            Debug.Assert(!string.IsNullOrEmpty(shareTemplate));

            using (var ds = DocumentStoreLocator.ResolveOrRoot(CommonConfiguration.CoreDatabaseRoute))
            {
                var subscr =
                    (ApplicationUserSubscription)
                    user.Extensions.GetOrAdd(ApplicationUserSubscription.Extension,
                                             _ => new ApplicationUserSubscription());
                bool updateUser = false;

                if (!string.IsNullOrEmpty(authCode))
                {
                    _dblog.InfoFormat("Attempting to sign up against authcode {0}", authCode);


                    var auth = ds.Include <AuthorizationCode>(ac => ac.Referent).Load(authCode);
                    if (null == auth)
                    {
                        throw new AuthCodeException(
                                  "Your authorization code is good, but payment has not been received yet. Please try again in a little while.");
                    }

                    var bp = ds.Load <BillingPlan>(auth.Referent);

                    if (null == bp)
                    {
                        throw new AuthCodeException(
                                  "There was a problem with your authorization code. Please email us about this so we can get your account up immediately.");
                    }

                    subscr.BillingPlan = bp;
                    updateUser         = true;
                }

                // user already subscribed or not?
                if (!user.AccountRoles.Contains(subRole))
                {
                    SendEmail email;

                    if (user.AccountRoles.Contains(unSubRole))
                    {
                        user.AccountRoles.Remove(unSubRole);
                    }

                    user.AccountRoles.Add(subRole);


                    if (subscr.SubscriptionStart == DateTime.MinValue)
                    {
                        subscr.SubscriptionStart = DateTime.UtcNow;
                    }

                    subscr.BillingStatus = BillingStatus.Billing;
                    updateUser           = true;


                    _log.InfoFormat("Signed user {0} to role {1}", user.PrincipalId, subRole);

                    var bp = subscr.BillingPlan;
                    if (null != bp && bp.AutoShare)
                    {
                        _log.InfoFormat("Distributing shared coupon for billing plan {0} to user {1}", bp.Name,
                                        user.PrincipalId);
                        Coupon sharedCoupon = new Coupon
                        {
                            BillingPlan  = bp,
                            CurrentCount = 0,
                            Expiration   = DateTime.UtcNow + TimeSpan.FromDays(7.0),
                            PassString   = GuidEncoder.Encode(Guid.NewGuid()),
                            TotalAllowed = 1
                        };

                        ds.Store(sharedCoupon);


                        email = SendEmail.CreateFromTemplate(_sender,
                                                             Enumerable.Repeat(user.ContactEmail, 1).ToArray(),
                                                             shareTemplate,
                                                             user.ContactFirstName,
                                                             sharedCoupon.PassString);
                    }
                    else
                    {
                        email = SendEmail.CreateFromTemplate(_sender,
                                                             Enumerable.Repeat(user.ContactEmail, 1).ToArray(),
                                                             emailTemplate,
                                                             user.ContactFirstName);
                    }


                    email.Send();
                }

                if (updateUser)
                {
                    ds.Store(user);
                }

                ds.SaveChanges();
            } // document session
        }
        public virtual string PromoteAccount(string couponPassKey, BrokerTransactionType tt, string newRole,
                                             bool resetRole = true)
        {
            Debug.Assert(!string.IsNullOrEmpty(couponPassKey));
            Debug.Assert(!string.IsNullOrEmpty(newRole));

            string retval = null;

            BillingPlan bp     = null;
            Coupon      coupon = null;

            if (!string.IsNullOrEmpty(couponPassKey))
            {
                if (GetBillingPlanFromCoupon(couponPassKey, out bp, out coupon))
                {
                    coupon.CurrentCount++;
                    _dblog.InfoFormat("Coupon {0} now used {1} times of {2}", coupon.PassString, coupon.CurrentCount,
                                      coupon.TotalAllowed);
                }
            }

            if (null == bp)
            {
                bp = GetDefaultBillingPlan();
                if (null == bp)
                {
                    throw new AccountPromotionException("No default billing plan is available.");
                }
            }

            bool requiresPayment = bp.RecurrenceType > RecurrenceTypes._Paid;

            IUserContext    uc   = null;
            ApplicationUser user = null;


            if (requiresPayment)
            {
                string trxInfo = null;
                if (tt == BrokerTransactionType.Express)
                {
                    _authCode = AuthorizationCode.GenerateCode();
                    trxInfo   = AuthCode;
                    _dblog.InfoFormat("Generated authcode {0} for express transaction button", trxInfo);
                }
                else if (tt == BrokerTransactionType.Upgrade)
                {
                    uc      = Catalog.Factory.Resolve <IUserContext>();
                    user    = uc.GetCurrentUser();
                    trxInfo = user.PrincipalId;

                    var subscr =
                        (ApplicationUserSubscription)user.Extensions.GetOrAdd(ApplicationUserSubscription.Extension,
                                                                              _ => new ApplicationUserSubscription());

                    subscr.BillingPlan = bp;
                    _dblog.InfoFormat("Generating upgrade payment button for user {0} to billing plan {1}",
                                      user.PrincipalId, bp.Name);
                }

                retval = BuildPaymentButton(trxInfo, bp, tt);
            }
            else
            {
                retval = null;
                uc     = Catalog.Factory.Resolve <IUserContext>();
                user   = uc.GetCurrentUser();

                var subscr =
                    (ApplicationUserSubscription)user.Extensions.GetOrAdd(ApplicationUserSubscription.Extension,
                                                                          _ => new ApplicationUserSubscription());

                Debug.Assert(null != user);
                if (null == user)
                {
                    throw new AccountPromotionException("User not signed in.");
                }

                subscr.BillingPlan = bp;

                switch (bp.RecurrenceType)
                {
                case RecurrenceTypes.FreeForLife:
                    if (resetRole)
                    {
                        user.AccountRoles.Clear();
                    }
                    user.AccountRoles.Add(newRole);
                    subscr.BillingPlan       = bp;
                    subscr.SubscriptionStart = DateTime.UtcNow;
                    subscr.SubscriptionEnd   = DateTime.MaxValue;
                    subscr.BillingStatus     = BillingStatus.NeverBilled;
                    _log.InfoFormat("User {0} promoted to free for life", user.PrincipalId);
                    break;

                case RecurrenceTypes.LimitedTrial:
                    if (subscr.HadTrialAccount == couponPassKey)
                    {
                        _log.WarnFormat("User {0} already used trial account for {1}", user.PrincipalId,
                                        couponPassKey);
                        throw new AccountPromotionException("Cannot use a trial account on this user.");
                    }

                    if (resetRole)
                    {
                        user.AccountRoles.Clear();
                    }
                    user.AccountRoles.Add(newRole);
                    subscr.SubscriptionStart = DateTime.UtcNow;
                    subscr.SubscriptionEnd   = DateTime.UtcNow + TimeSpan.FromDays(bp.GracePeriodDays);
                    subscr.HadTrialAccount   = couponPassKey;
                    subscr.BillingStatus     = BillingStatus.LimitedTrial;
                    _log.InfoFormat("user {0} promoted to limited trial until {1} using coupon {2}",
                                    user.PrincipalId, subscr.SubscriptionEnd, couponPassKey);
                    break;

                case RecurrenceTypes.Owner:
                    user.AccountRoles.Add(newRole);
                    subscr.BillingStatus = BillingStatus.NeverBilled;
                    _log.WarnFormat("user {0} promoted to owner", user.PrincipalId);
                    break;

                default:
                    var bad = string.Format("Billing plan {0} has invalid recurrence type {1}", bp.Name,
                                            bp.RecurrenceType.ToString());
                    _log.ErrorFormat(bad);
                    throw new AccessViolationException(bad);
                    //break;
                }
            }

            using (var ds = DocumentStoreLocator.ResolveOrRoot(CommonConfiguration.CoreDatabaseRoute))
            {
                // user
                if (null != user)
                {
                    ds.Store(user);
                }

                // coupon
                if (null != coupon)
                {
                    ds.Store(coupon);
                }

                ds.SaveChanges();
            }


            return(retval);
        }