ducky/subscriptions

Paddy 2015-06-22 Parent:61c4ce5850da Child:36e90e828dd0

3:b240b6123548 Browse Files

Export all subscriptionStore methods. We're not going to wrap all our subscriptionStore interactions in a Context type, so we need to expose all the functions so other packages can call them. Also, it's now SubscriptionStore. Also creates a SubscriptionRequest type that is used to create a new Subscription instance, along with a Validate method on it, to detect errors when creating a SubscriptionRequest. Update our CreateSubscription function to be New, and have it take a SubscriptionRequest, a Stripe instance, and a SubscriptionStore as arguments. It'll create the Subscription in the SubscriptionStore, create a customer in Stripe, and create the subscription in Stripe, then associate the Stripe subscription with the created Subscription. Also, fix our StripeSubscriptionChange function to correctly equate a missing/zero Unix timestamp from Stripe with a missing/zero time.Time.

.hgignore memstore.go stripe.go subscription.go subscription_memstore.go subscription_postgres.go subscription_store_test.go

     1.1 --- a/.hgignore	Tue Jun 16 23:09:59 2015 -0400
     1.2 +++ b/.hgignore	Mon Jun 22 18:34:07 2015 -0400
     1.3 @@ -1,2 +1,3 @@
     1.4  cover.out
     1.5  .swp
     1.6 +subscriptionsd/subscriptionsd
     2.1 --- a/memstore.go	Tue Jun 16 23:09:59 2015 -0400
     2.2 +++ b/memstore.go	Mon Jun 22 18:34:07 2015 -0400
     2.3 @@ -19,7 +19,7 @@
     2.4  	}
     2.5  }
     2.6  
     2.7 -func (m *Memstore) reset() error {
     2.8 +func (m *Memstore) Reset() error {
     2.9  	m.subscriptionLock.Lock()
    2.10  	defer m.subscriptionLock.Unlock()
    2.11  
     3.1 --- a/stripe.go	Tue Jun 16 23:09:59 2015 -0400
     3.2 +++ b/stripe.go	Mon Jun 22 18:34:07 2015 -0400
     3.3 @@ -1,6 +1,7 @@
     3.4  package subscriptions
     3.5  
     3.6  import (
     3.7 +	"log"
     3.8  	"time"
     3.9  
    3.10  	"code.secondbit.org/uuid.hg"
    3.11 @@ -58,19 +59,20 @@
    3.12  	return resp, nil
    3.13  }
    3.14  
    3.15 -func CreateSubscription(token, email string, subscription Subscription, s Stripe, store subscriptionStore) error {
    3.16 +func New(req SubscriptionRequest, s Stripe, store SubscriptionStore) (Subscription, error) {
    3.17 +	subscription := SubscriptionFromRequest(req)
    3.18  	// create the subscription in our datastore
    3.19  	// this will fail if they already have a subscription, which prevents duplicate/orphaned Stripe customers being created
    3.20 -	err := store.createSubscription(subscription)
    3.21 +	err := store.CreateSubscription(subscription)
    3.22  	if err != nil {
    3.23 -		return err
    3.24 +		return subscription, err
    3.25  	}
    3.26  
    3.27  	// create the customer in Stripe, storing the token for reuse
    3.28 -	customer, err := CreateStripeCustomer(token, email, subscription.UserID, s)
    3.29 +	customer, err := CreateStripeCustomer(req.StripeToken, req.Email, req.UserID, s)
    3.30  	if err != nil {
    3.31  		// TODO: delete subscription object
    3.32 -		return err
    3.33 +		return subscription, err
    3.34  	}
    3.35  
    3.36  	// create the subscription in Stripe, storing the ID for tracking and associating purposes
    3.37 @@ -78,17 +80,18 @@
    3.38  	if err != nil {
    3.39  		// TODO: delete customer
    3.40  		// TODO: delete subscription object
    3.41 -		return err
    3.42 +		return subscription, err
    3.43  	}
    3.44  
    3.45  	// update our subscription in the datastore with the latest information from Stripe
    3.46  	change := StripeSubscriptionChange(subscription, *stripeSub)
    3.47 -	err = store.updateSubscription(subscription.UserID, change)
    3.48 +	err = store.UpdateSubscription(subscription.UserID, change)
    3.49  	if err != nil {
    3.50 -		// TODO: log an error, manually retry later?
    3.51 -		return err
    3.52 +		log.Printf("Error pairing Stripe subscription %s to user %s: %+v\nUser needs to have their subscription updated manually.", change.StripeSubscription, req.UserID, err)
    3.53 +		return subscription, nil
    3.54  	}
    3.55 -	return nil
    3.56 +	subscription.ApplyChange(change)
    3.57 +	return subscription, nil
    3.58  }
    3.59  
    3.60  func StripeSubscriptionChange(orig Subscription, subscription stripe.Sub) SubscriptionChange {
    3.61 @@ -103,23 +106,23 @@
    3.62  	if subscription.EndCancel != orig.Canceling {
    3.63  		change.Canceling = &subscription.EndCancel
    3.64  	}
    3.65 -	if !time.Unix(subscription.TrialStart, 0).Equal(orig.TrialStart) {
    3.66 +	if !time.Unix(subscription.TrialStart, 0).Equal(orig.TrialStart) && !(subscription.TrialStart == 0 && orig.TrialStart.IsZero()) {
    3.67  		trialStart := time.Unix(subscription.TrialStart, 0)
    3.68  		change.TrialStart = &trialStart
    3.69  	}
    3.70 -	if !time.Unix(subscription.TrialEnd, 0).Equal(orig.TrialEnd) {
    3.71 +	if !time.Unix(subscription.TrialEnd, 0).Equal(orig.TrialEnd) && !(subscription.TrialEnd == 0 && orig.TrialEnd.IsZero()) {
    3.72  		trialEnd := time.Unix(subscription.TrialEnd, 0)
    3.73  		change.TrialEnd = &trialEnd
    3.74  	}
    3.75 -	if !time.Unix(subscription.PeriodStart, 0).Equal(orig.PeriodStart) {
    3.76 +	if !time.Unix(subscription.PeriodStart, 0).Equal(orig.PeriodStart) && !(subscription.PeriodStart == 0 && orig.PeriodStart.IsZero()) {
    3.77  		periodStart := time.Unix(subscription.PeriodStart, 0)
    3.78  		change.PeriodStart = &periodStart
    3.79  	}
    3.80 -	if !time.Unix(subscription.PeriodEnd, 0).Equal(orig.PeriodEnd) {
    3.81 +	if !time.Unix(subscription.PeriodEnd, 0).Equal(orig.PeriodEnd) && !(subscription.PeriodEnd == 0 && orig.PeriodEnd.IsZero()) {
    3.82  		periodEnd := time.Unix(subscription.PeriodEnd, 0)
    3.83  		change.PeriodEnd = &periodEnd
    3.84  	}
    3.85 -	if !time.Unix(subscription.Canceled, 0).Equal(orig.CanceledAt) {
    3.86 +	if !time.Unix(subscription.Canceled, 0).Equal(orig.CanceledAt) && !(subscription.Canceled == 0 && orig.CanceledAt.IsZero()) {
    3.87  		canceledAt := time.Unix(subscription.Canceled, 0)
    3.88  		change.CanceledAt = &canceledAt
    3.89  	}
     4.1 --- a/subscription.go	Tue Jun 16 23:09:59 2015 -0400
     4.2 +++ b/subscription.go	Mon Jun 22 18:34:07 2015 -0400
     4.3 @@ -4,6 +4,7 @@
     4.4  	"errors"
     4.5  	"time"
     4.6  
     4.7 +	"code.secondbit.org/api.hg"
     4.8  	"code.secondbit.org/uuid.hg"
     4.9  )
    4.10  
    4.11 @@ -25,6 +26,10 @@
    4.12  	// ErrNoSubscriptionID is returned when one or more Subscription IDs
    4.13  	// are required, but none are provided.
    4.14  	ErrNoSubscriptionID = errors.New("no Subscription ID provided")
    4.15 +
    4.16 +	planOptions = []string{"basic_monthly", "basic_yearly", "supporter_monthly", "supporter_yearly"}
    4.17 +
    4.18 +	Version string
    4.19  )
    4.20  
    4.21  // Subscription represents the state of a user's payments. It holds
    4.22 @@ -32,20 +37,20 @@
    4.23  // should be charged, how to charge a user and how much to charge
    4.24  // the user.
    4.25  type Subscription struct {
    4.26 -	UserID               uuid.ID
    4.27 -	StripeSubscription   string
    4.28 -	Plan                 string
    4.29 -	Status               string
    4.30 -	Canceling            bool
    4.31 -	Created              time.Time
    4.32 -	TrialStart           time.Time
    4.33 -	TrialEnd             time.Time
    4.34 -	PeriodStart          time.Time
    4.35 -	PeriodEnd            time.Time
    4.36 -	CanceledAt           time.Time
    4.37 -	FailedChargeAttempts int
    4.38 -	LastFailedCharge     time.Time
    4.39 -	LastNotified         time.Time
    4.40 +	UserID               uuid.ID   `json:"user_id"`
    4.41 +	StripeSubscription   string    `json:"stripe_subscription"`
    4.42 +	Plan                 string    `json:"plan"`
    4.43 +	Status               string    `json:"status"`
    4.44 +	Canceling            bool      `json:"canceling"`
    4.45 +	Created              time.Time `json:"created"`
    4.46 +	TrialStart           time.Time `json:"trial_start,omitempty"`
    4.47 +	TrialEnd             time.Time `json:"trial_end,omitempty"`
    4.48 +	PeriodStart          time.Time `json:"period_start,omitempty"`
    4.49 +	PeriodEnd            time.Time `json:"period_end,omitempty"`
    4.50 +	CanceledAt           time.Time `json:"canceled_at,omitempty"`
    4.51 +	FailedChargeAttempts int       `json:"failed_charge_attempts"`
    4.52 +	LastFailedCharge     time.Time `json:"last_failed_charge,omitempty"`
    4.53 +	LastNotified         time.Time `json:"last_notified,omitempty"`
    4.54  }
    4.55  
    4.56  // SubscriptionChange represents desired changes to a Subscription
    4.57 @@ -148,6 +153,53 @@
    4.58  	}
    4.59  }
    4.60  
    4.61 +type SubscriptionRequest struct {
    4.62 +	UserID      uuid.ID `json:"user_id"`
    4.63 +	Email       string  `json:"email"`
    4.64 +	StripeToken string  `json:"stripe_token"`
    4.65 +	Plan        string  `json:"plan"`
    4.66 +}
    4.67 +
    4.68 +func (req SubscriptionRequest) Validate(user uuid.ID, admin bool) []api.RequestError {
    4.69 +	var errors []api.RequestError
    4.70 +	if req.UserID == nil {
    4.71 +		errors = append(errors, api.RequestError{Slug: api.RequestErrMissing, Field: "/user_id"})
    4.72 +	} else if !req.UserID.Equal(user) && !admin {
    4.73 +		errors = append(errors, api.RequestError{Slug: api.RequestErrAccessDenied, Field: "/user_id"})
    4.74 +	}
    4.75 +	if req.Email == "" {
    4.76 +		errors = append(errors, api.RequestError{Slug: api.RequestErrMissing, Field: "/email"})
    4.77 +	}
    4.78 +	if req.StripeToken == "" {
    4.79 +		errors = append(errors, api.RequestError{Slug: api.RequestErrMissing, Field: "/stripe_token"})
    4.80 +	}
    4.81 +	if req.Plan == "" {
    4.82 +		errors = append(errors, api.RequestError{Slug: api.RequestErrMissing, Field: "/plan"})
    4.83 +	} else {
    4.84 +		var acceptablePlan bool
    4.85 +		for _, p := range planOptions {
    4.86 +			if req.Plan == p {
    4.87 +				acceptablePlan = true
    4.88 +				break
    4.89 +			}
    4.90 +		}
    4.91 +		if !acceptablePlan {
    4.92 +			errors = append(errors, api.RequestError{Slug: api.RequestErrInvalidValue, Field: "/plan"})
    4.93 +		}
    4.94 +	}
    4.95 +	return errors
    4.96 +}
    4.97 +
    4.98 +func SubscriptionFromRequest(req SubscriptionRequest) Subscription {
    4.99 +	return Subscription{
   4.100 +		UserID:             req.UserID,
   4.101 +		StripeSubscription: req.StripeToken,
   4.102 +		Plan:               req.Plan,
   4.103 +		Created:            time.Now(),
   4.104 +		TrialStart:         time.Now(),
   4.105 +	}
   4.106 +}
   4.107 +
   4.108  // SubscriptionStats represents a set of statistics about our Subscription
   4.109  // data that will be exposed to the Prometheus scraper.
   4.110  type SubscriptionStats struct {
   4.111 @@ -170,11 +222,11 @@
   4.112  	// was omitted."
   4.113  }
   4.114  
   4.115 -type subscriptionStore interface {
   4.116 -	reset() error
   4.117 -	createSubscription(sub Subscription) error
   4.118 -	updateSubscription(id uuid.ID, change SubscriptionChange) error
   4.119 -	deleteSubscription(id uuid.ID) error
   4.120 -	getSubscriptions(ids []uuid.ID) (map[string]Subscription, error)
   4.121 -	getSubscriptionStats() (SubscriptionStats, error)
   4.122 +type SubscriptionStore interface {
   4.123 +	Reset() error
   4.124 +	CreateSubscription(sub Subscription) error
   4.125 +	UpdateSubscription(id uuid.ID, change SubscriptionChange) error
   4.126 +	DeleteSubscription(id uuid.ID) error
   4.127 +	GetSubscriptions(ids []uuid.ID) (map[string]Subscription, error)
   4.128 +	GetSubscriptionStats() (SubscriptionStats, error)
   4.129  }
     5.1 --- a/subscription_memstore.go	Tue Jun 16 23:09:59 2015 -0400
     5.2 +++ b/subscription_memstore.go	Mon Jun 22 18:34:07 2015 -0400
     5.3 @@ -13,7 +13,7 @@
     5.4  	return false
     5.5  }
     5.6  
     5.7 -func (m *Memstore) createSubscription(sub Subscription) error {
     5.8 +func (m *Memstore) CreateSubscription(sub Subscription) error {
     5.9  	m.subscriptionLock.Lock()
    5.10  	defer m.subscriptionLock.Unlock()
    5.11  
    5.12 @@ -27,7 +27,7 @@
    5.13  	return nil
    5.14  }
    5.15  
    5.16 -func (m *Memstore) updateSubscription(id uuid.ID, change SubscriptionChange) error {
    5.17 +func (m *Memstore) UpdateSubscription(id uuid.ID, change SubscriptionChange) error {
    5.18  	if change.IsEmpty() {
    5.19  		return ErrSubscriptionChangeEmpty
    5.20  	}
    5.21 @@ -49,7 +49,7 @@
    5.22  	return nil
    5.23  }
    5.24  
    5.25 -func (m *Memstore) deleteSubscription(id uuid.ID) error {
    5.26 +func (m *Memstore) DeleteSubscription(id uuid.ID) error {
    5.27  	m.subscriptionLock.Lock()
    5.28  	defer m.subscriptionLock.Unlock()
    5.29  
    5.30 @@ -61,7 +61,7 @@
    5.31  	return nil
    5.32  }
    5.33  
    5.34 -func (m *Memstore) getSubscriptions(ids []uuid.ID) (map[string]Subscription, error) {
    5.35 +func (m *Memstore) GetSubscriptions(ids []uuid.ID) (map[string]Subscription, error) {
    5.36  	if len(ids) < 1 {
    5.37  		return map[string]Subscription{}, ErrNoSubscriptionID
    5.38  	}
    5.39 @@ -80,7 +80,7 @@
    5.40  	return result, nil
    5.41  }
    5.42  
    5.43 -func (m *Memstore) getSubscriptionStats() (SubscriptionStats, error) {
    5.44 +func (m *Memstore) GetSubscriptionStats() (SubscriptionStats, error) {
    5.45  	m.subscriptionLock.RLock()
    5.46  	defer m.subscriptionLock.RUnlock()
    5.47  
     6.1 --- a/subscription_postgres.go	Tue Jun 16 23:09:59 2015 -0400
     6.2 +++ b/subscription_postgres.go	Mon Jun 22 18:34:07 2015 -0400
     6.3 @@ -21,7 +21,7 @@
     6.4  	return query.FlushExpressions(" ")
     6.5  }
     6.6  
     6.7 -func (p Postgres) reset() error {
     6.8 +func (p Postgres) Reset() error {
     6.9  	query := p.resetSQL()
    6.10  	_, err := p.Exec(query.String(), query.Args...)
    6.11  	if err != nil {
    6.12 @@ -39,7 +39,7 @@
    6.13  	return query.FlushExpressions(" ")
    6.14  }
    6.15  
    6.16 -func (p Postgres) createSubscription(sub Subscription) error {
    6.17 +func (p Postgres) CreateSubscription(sub Subscription) error {
    6.18  	query := p.createSubscriptionSQL(sub)
    6.19  	_, err := p.Exec(query.String(), query.Args...)
    6.20  	if e, ok := err.(*pq.Error); ok && e.Constraint == "subscriptions_pkey" {
    6.21 @@ -71,7 +71,7 @@
    6.22  	return query.FlushExpressions(" ")
    6.23  }
    6.24  
    6.25 -func (p Postgres) updateSubscription(id uuid.ID, change SubscriptionChange) error {
    6.26 +func (p Postgres) UpdateSubscription(id uuid.ID, change SubscriptionChange) error {
    6.27  	if change.IsEmpty() {
    6.28  		return ErrSubscriptionChangeEmpty
    6.29  	}
    6.30 @@ -101,7 +101,7 @@
    6.31  	return query.FlushExpressions(" ")
    6.32  }
    6.33  
    6.34 -func (p Postgres) deleteSubscription(id uuid.ID) error {
    6.35 +func (p Postgres) DeleteSubscription(id uuid.ID) error {
    6.36  	query := p.deleteSubscriptionSQL(id)
    6.37  	res, err := p.Exec(query.String(), query.Args...)
    6.38  	if err != nil {
    6.39 @@ -131,7 +131,7 @@
    6.40  	return query.FlushExpressions(" ")
    6.41  }
    6.42  
    6.43 -func (p Postgres) getSubscriptions(ids []uuid.ID) (map[string]Subscription, error) {
    6.44 +func (p Postgres) GetSubscriptions(ids []uuid.ID) (map[string]Subscription, error) {
    6.45  	results := map[string]Subscription{}
    6.46  	if len(ids) < 1 {
    6.47  		return results, ErrNoSubscriptionID
    6.48 @@ -190,7 +190,7 @@
    6.49  	return query.FlushExpressions(" ")
    6.50  }
    6.51  
    6.52 -func (p Postgres) getSubscriptionStats() (SubscriptionStats, error) {
    6.53 +func (p Postgres) GetSubscriptionStats() (SubscriptionStats, error) {
    6.54  	stats := SubscriptionStats{
    6.55  		Plans: map[string]int64{},
    6.56  	}
     7.1 --- a/subscription_store_test.go	Tue Jun 16 23:09:59 2015 -0400
     7.2 +++ b/subscription_store_test.go	Mon Jun 22 18:34:07 2015 -0400
     7.3 @@ -34,7 +34,7 @@
     7.4  	}
     7.5  }
     7.6  
     7.7 -var testSubscriptionStores = []subscriptionStore{
     7.8 +var testSubscriptionStores = []SubscriptionStore{
     7.9  	NewMemstore(),
    7.10  }
    7.11  
    7.12 @@ -124,7 +124,7 @@
    7.13  
    7.14  func TestCreateSubscription(t *testing.T) {
    7.15  	for _, store := range testSubscriptionStores {
    7.16 -		err := store.reset()
    7.17 +		err := store.Reset()
    7.18  		if err != nil {
    7.19  			t.Fatalf("Error resetting %T: %+v\n", store, err)
    7.20  		}
    7.21 @@ -136,11 +136,11 @@
    7.22  			TrialStart:         time.Now().Round(time.Millisecond),
    7.23  			TrialEnd:           time.Now().Round(time.Millisecond).Add(time.Hour * 24 * 31),
    7.24  		}
    7.25 -		err = store.createSubscription(sub)
    7.26 +		err = store.CreateSubscription(sub)
    7.27  		if err != nil {
    7.28  			t.Errorf("Error creating subscription in %T: %+v\n", store, err)
    7.29  		}
    7.30 -		retrieved, err := store.getSubscriptions([]uuid.ID{sub.UserID})
    7.31 +		retrieved, err := store.GetSubscriptions([]uuid.ID{sub.UserID})
    7.32  		if err != nil {
    7.33  			t.Errorf("Error retrieving subscription from %T: %+v\n", store, err)
    7.34  		}
    7.35 @@ -151,17 +151,17 @@
    7.36  		if !ok {
    7.37  			t.Errorf("Expected %s to be %v, got %v from %T\n", field, expected, result, store)
    7.38  		}
    7.39 -		err = store.createSubscription(sub)
    7.40 +		err = store.CreateSubscription(sub)
    7.41  		if err != ErrSubscriptionAlreadyExists {
    7.42  			t.Errorf("Unexpected error creating subscription in %T (wanted %+v): %+v\n", store, ErrSubscriptionAlreadyExists, err)
    7.43  		}
    7.44  		sub.UserID = uuid.NewID()
    7.45 -		err = store.createSubscription(sub)
    7.46 +		err = store.CreateSubscription(sub)
    7.47  		if err != ErrStripeSubscriptionAlreadyExists {
    7.48  			t.Errorf("Unexpected error creating subscription in %T (wanted %+v): %#+v\n", store, ErrStripeSubscriptionAlreadyExists, err)
    7.49  		}
    7.50  		sub.StripeSubscription = "stripeSubscription2"
    7.51 -		err = store.createSubscription(sub)
    7.52 +		err = store.CreateSubscription(sub)
    7.53  		if err != nil {
    7.54  			t.Errorf("Error creating subscription in %T: %+v\n", store, err)
    7.55  		}
    7.56 @@ -188,11 +188,11 @@
    7.57  	}
    7.58  
    7.59  	for _, store := range testSubscriptionStores {
    7.60 -		err := store.reset()
    7.61 +		err := store.Reset()
    7.62  		if err != nil {
    7.63  			t.Fatalf("Error resetting %T: %+v\n", store, err)
    7.64  		}
    7.65 -		err = store.createSubscription(sub)
    7.66 +		err = store.CreateSubscription(sub)
    7.67  		if err != nil {
    7.68  			t.Fatalf("Error saving subscription in %T: %s\n", store, err)
    7.69  		}
    7.70 @@ -292,11 +292,11 @@
    7.71  			if !match {
    7.72  				t.Errorf("Expected field `%s` to be `%v`, got `%v`\n", field, expected, got)
    7.73  			}
    7.74 -			err = store.updateSubscription(sub.UserID, change)
    7.75 +			err = store.UpdateSubscription(sub.UserID, change)
    7.76  			if err != nil {
    7.77  				t.Errorf("Error updating subscription in %T: %s\n", store, err)
    7.78  			}
    7.79 -			retrieved, err := store.getSubscriptions([]uuid.ID{sub.UserID})
    7.80 +			retrieved, err := store.GetSubscriptions([]uuid.ID{sub.UserID})
    7.81  			if err != nil {
    7.82  				t.Errorf("Error getting subscription from %T: %s\n", store, err)
    7.83  			}
    7.84 @@ -311,22 +311,22 @@
    7.85  			sub = result
    7.86  		}
    7.87  
    7.88 -		err = store.createSubscription(sub2)
    7.89 +		err = store.CreateSubscription(sub2)
    7.90  		if err != nil {
    7.91  			t.Fatalf("Error saving subscription in %T: %+v\n", store, err)
    7.92  		}
    7.93  		change := SubscriptionChange{}
    7.94 -		err = store.updateSubscription(sub.UserID, change)
    7.95 +		err = store.UpdateSubscription(sub.UserID, change)
    7.96  		if err != ErrSubscriptionChangeEmpty {
    7.97  			t.Errorf("Expected err to be %+v, but got %+v from %T\n", ErrSubscriptionChangeEmpty, err, store)
    7.98  		}
    7.99  		stripeSubscription := sub2.StripeSubscription
   7.100  		change.StripeSubscription = &stripeSubscription
   7.101 -		err = store.updateSubscription(uuid.NewID(), change)
   7.102 +		err = store.UpdateSubscription(uuid.NewID(), change)
   7.103  		if err != ErrSubscriptionNotFound {
   7.104  			t.Errorf("Expected err to be %+v, but got %+v from %T\n", ErrSubscriptionNotFound, err, store)
   7.105  		}
   7.106 -		err = store.updateSubscription(sub.UserID, change)
   7.107 +		err = store.UpdateSubscription(sub.UserID, change)
   7.108  		if err != ErrStripeSubscriptionAlreadyExists {
   7.109  			t.Errorf("Expected err to be %+v, but got %+v from %T\n", ErrStripeSubscriptionAlreadyExists, err, store)
   7.110  		}
   7.111 @@ -335,7 +335,7 @@
   7.112  
   7.113  func TestDeleteSubscription(t *testing.T) {
   7.114  	for _, store := range testSubscriptionStores {
   7.115 -		err := store.reset()
   7.116 +		err := store.Reset()
   7.117  		if err != nil {
   7.118  			t.Fatalf("Error resetting %T: %+v\n", store, err)
   7.119  		}
   7.120 @@ -347,19 +347,19 @@
   7.121  			UserID:             uuid.NewID(),
   7.122  			StripeSubscription: "stripeSubscription2",
   7.123  		}
   7.124 -		err = store.createSubscription(sub1)
   7.125 +		err = store.CreateSubscription(sub1)
   7.126  		if err != nil {
   7.127  			t.Fatalf("Error creating %+v in %T: %+v\n", sub1, store, err)
   7.128  		}
   7.129 -		err = store.createSubscription(sub2)
   7.130 +		err = store.CreateSubscription(sub2)
   7.131  		if err != nil {
   7.132  			t.Fatalf("Error creating %+v in %T: %+v\n", sub2, store, err)
   7.133  		}
   7.134 -		err = store.deleteSubscription(sub1.UserID)
   7.135 +		err = store.DeleteSubscription(sub1.UserID)
   7.136  		if err != nil {
   7.137  			t.Fatalf("Error deleting %+v in %T: %+v\n", sub1, store, err)
   7.138  		}
   7.139 -		retrieved, err := store.getSubscriptions([]uuid.ID{sub1.UserID, sub2.UserID})
   7.140 +		retrieved, err := store.GetSubscriptions([]uuid.ID{sub1.UserID, sub2.UserID})
   7.141  		if err != nil {
   7.142  			t.Errorf("Error retrieving subscriptions from %T: %+v\n", store, err)
   7.143  		}
   7.144 @@ -371,7 +371,7 @@
   7.145  		if !ok {
   7.146  			t.Errorf("Expected to retrieve %s from %T, but missing was %+v\n", sub2.UserID.String(), store, missing)
   7.147  		}
   7.148 -		err = store.deleteSubscription(sub1.UserID)
   7.149 +		err = store.DeleteSubscription(sub1.UserID)
   7.150  		if err != ErrSubscriptionNotFound {
   7.151  			t.Errorf("Expected err to be %+v, but got %+v from %T\n", ErrSubscriptionNotFound, err, store)
   7.152  		}
   7.153 @@ -380,7 +380,7 @@
   7.154  
   7.155  func TestGetSubscriptions(t *testing.T) {
   7.156  	for _, store := range testSubscriptionStores {
   7.157 -		err := store.reset()
   7.158 +		err := store.Reset()
   7.159  		if err != nil {
   7.160  			t.Fatalf("Error resetting %T: %+v\n", store, err)
   7.161  		}
   7.162 @@ -411,23 +411,23 @@
   7.163  			PeriodEnd:          time.Now().Round(time.Millisecond),
   7.164  			Status:             "unpaid",
   7.165  		}
   7.166 -		err = store.createSubscription(sub1)
   7.167 +		err = store.CreateSubscription(sub1)
   7.168  		if err != nil {
   7.169  			t.Fatalf("Error creating %+v in %T: %+v\n", sub1, store, err)
   7.170  		}
   7.171 -		err = store.createSubscription(sub2)
   7.172 +		err = store.CreateSubscription(sub2)
   7.173  		if err != nil {
   7.174  			t.Fatalf("Error creating %+v in %T: %+v\n", sub1, store, err)
   7.175  		}
   7.176 -		err = store.createSubscription(sub3)
   7.177 +		err = store.CreateSubscription(sub3)
   7.178  		if err != nil {
   7.179  			t.Fatalf("Error creating %+v in %T: %+v\n", sub1, store, err)
   7.180  		}
   7.181 -		retrieved, err := store.getSubscriptions([]uuid.ID{})
   7.182 +		retrieved, err := store.GetSubscriptions([]uuid.ID{})
   7.183  		if err != ErrNoSubscriptionID {
   7.184  			t.Errorf("Error retrieving no subscriptions from %T. Expected %+v, got %+v\n", store, ErrNoSubscriptionID, err)
   7.185  		}
   7.186 -		retrieved, err = store.getSubscriptions([]uuid.ID{sub1.UserID})
   7.187 +		retrieved, err = store.GetSubscriptions([]uuid.ID{sub1.UserID})
   7.188  		if err != nil {
   7.189  			t.Errorf("Error retrieving %s from %T: %+v\n", sub1.UserID, store, err)
   7.190  		}
   7.191 @@ -436,7 +436,7 @@
   7.192  			t.Logf("Results: %+v\n", retrieved)
   7.193  			t.Errorf("Expected %+v to be in the results, was not for %T.\n", missing, store)
   7.194  		}
   7.195 -		retrieved, err = store.getSubscriptions([]uuid.ID{sub1.UserID, sub2.UserID})
   7.196 +		retrieved, err = store.GetSubscriptions([]uuid.ID{sub1.UserID, sub2.UserID})
   7.197  		if err != nil {
   7.198  			t.Errorf("Error retrieving %s and %s from %T: %+v\n", sub1.UserID, sub2.UserID, store, err)
   7.199  		}
   7.200 @@ -445,7 +445,7 @@
   7.201  			t.Logf("Results: %+v\n", retrieved)
   7.202  			t.Errorf("Expected %+v to be in the results, was not for %T.\n", missing, store)
   7.203  		}
   7.204 -		retrieved, err = store.getSubscriptions([]uuid.ID{sub1.UserID, sub3.UserID})
   7.205 +		retrieved, err = store.GetSubscriptions([]uuid.ID{sub1.UserID, sub3.UserID})
   7.206  		if err != nil {
   7.207  			t.Errorf("Error retrieving %s and %s from %T: %+v\n", sub1.UserID, sub3.UserID, store, err)
   7.208  		}
   7.209 @@ -454,7 +454,7 @@
   7.210  			t.Logf("Results: %+v\n", retrieved)
   7.211  			t.Errorf("Expected %+v to be in the results, was not for %T.\n", missing, store)
   7.212  		}
   7.213 -		retrieved, err = store.getSubscriptions([]uuid.ID{sub1.UserID, sub2.UserID, sub3.UserID})
   7.214 +		retrieved, err = store.GetSubscriptions([]uuid.ID{sub1.UserID, sub2.UserID, sub3.UserID})
   7.215  		if err != nil {
   7.216  			t.Errorf("Error retrieving %s, %s, and %s from %T: %+v\n", sub1.UserID, sub2.UserID, sub3.UserID, store, err)
   7.217  		}
   7.218 @@ -463,7 +463,7 @@
   7.219  			t.Logf("Results: %+v\n", retrieved)
   7.220  			t.Errorf("Expected %+v to be in the results, was not for %T.\n", missing, store)
   7.221  		}
   7.222 -		retrieved, err = store.getSubscriptions([]uuid.ID{sub2.UserID})
   7.223 +		retrieved, err = store.GetSubscriptions([]uuid.ID{sub2.UserID})
   7.224  		if err != nil {
   7.225  			t.Errorf("Error retrieving %s from %T: %+v\n", sub2.UserID, store, err)
   7.226  		}
   7.227 @@ -472,7 +472,7 @@
   7.228  			t.Logf("Results: %+v\n", retrieved)
   7.229  			t.Errorf("Expected %+v to be in the results, was not for %T.\n", missing, store)
   7.230  		}
   7.231 -		retrieved, err = store.getSubscriptions([]uuid.ID{sub2.UserID, sub3.UserID})
   7.232 +		retrieved, err = store.GetSubscriptions([]uuid.ID{sub2.UserID, sub3.UserID})
   7.233  		if err != nil {
   7.234  			t.Errorf("Error retrieving %s and %s from %T: %+v\n", sub2.UserID, sub3.UserID, store, err)
   7.235  		}
   7.236 @@ -481,7 +481,7 @@
   7.237  			t.Logf("Results: %+v\n", retrieved)
   7.238  			t.Errorf("Expected %+v to be in the results, was not for %T.\n", missing, store)
   7.239  		}
   7.240 -		retrieved, err = store.getSubscriptions([]uuid.ID{sub3.UserID})
   7.241 +		retrieved, err = store.GetSubscriptions([]uuid.ID{sub3.UserID})
   7.242  		if err != nil {
   7.243  			t.Errorf("Error retrieving %s from %T: %+v\n", sub3.UserID, store, err)
   7.244  		}
   7.245 @@ -490,14 +490,14 @@
   7.246  			t.Logf("Results: %+v\n", retrieved)
   7.247  			t.Errorf("Expected %+v to be in the results, was not for %T.\n", missing, store)
   7.248  		}
   7.249 -		retrieved, err = store.getSubscriptions([]uuid.ID{uuid.NewID()})
   7.250 +		retrieved, err = store.GetSubscriptions([]uuid.ID{uuid.NewID()})
   7.251  		if err != nil {
   7.252  			t.Errorf("Error retrieving non-existent ID from %T: %+v\n", store, err)
   7.253  		}
   7.254  		if len(retrieved) != 0 {
   7.255  			t.Errorf("Expected no results, %T returned %+v\n", store, retrieved)
   7.256  		}
   7.257 -		retrieved, err = store.getSubscriptions([]uuid.ID{sub1.UserID, sub2.UserID, uuid.NewID(), sub3.UserID})
   7.258 +		retrieved, err = store.GetSubscriptions([]uuid.ID{sub1.UserID, sub2.UserID, uuid.NewID(), sub3.UserID})
   7.259  		if err != nil {
   7.260  			t.Errorf("Error retrieving non-existent ID from %T: %+v\n", store, err)
   7.261  		}
   7.262 @@ -514,7 +514,7 @@
   7.263  
   7.264  func TestGetSubscriptionStats(t *testing.T) {
   7.265  	for _, store := range testSubscriptionStores {
   7.266 -		err := store.reset()
   7.267 +		err := store.Reset()
   7.268  		if err != nil {
   7.269  			t.Fatalf("Error resetting %T: %+v\n", store, err)
   7.270  		}
   7.271 @@ -530,11 +530,11 @@
   7.272  			Plan:               "plan2",
   7.273  			Status:             "past_due",
   7.274  		}
   7.275 -		err = store.createSubscription(sub1)
   7.276 +		err = store.CreateSubscription(sub1)
   7.277  		if err != nil {
   7.278  			t.Fatalf("Error creating %+v in %T: %+v\n", sub1, store, err)
   7.279  		}
   7.280 -		stats, err := store.getSubscriptionStats()
   7.281 +		stats, err := store.GetSubscriptionStats()
   7.282  		if err != nil {
   7.283  			t.Errorf("Error getting stats from %T: %+v\n", store, err)
   7.284  		}
   7.285 @@ -549,11 +549,11 @@
   7.286  		if !ok {
   7.287  			t.Errorf("Expected %s to be %+v, got %+v from %T\n", field, expected, results, store)
   7.288  		}
   7.289 -		err = store.createSubscription(sub2)
   7.290 +		err = store.CreateSubscription(sub2)
   7.291  		if err != nil {
   7.292  			t.Fatalf("Error creating %+v in %T: %+v\n", sub2, store, err)
   7.293  		}
   7.294 -		stats, err = store.getSubscriptionStats()
   7.295 +		stats, err = store.GetSubscriptionStats()
   7.296  		if err != nil {
   7.297  			t.Errorf("Error getting status from %T: %+v\n", store, err)
   7.298  		}
   7.299 @@ -569,11 +569,11 @@
   7.300  		if !ok {
   7.301  			t.Errorf("Expected %s to be %+v, got %+v from %T\n", field, expected, results, store)
   7.302  		}
   7.303 -		err = store.deleteSubscription(sub1.UserID)
   7.304 +		err = store.DeleteSubscription(sub1.UserID)
   7.305  		if err != nil {
   7.306  			t.Errorf("Error deleting subscription from %T: %+v\n", store, err)
   7.307  		}
   7.308 -		stats, err = store.getSubscriptionStats()
   7.309 +		stats, err = store.GetSubscriptionStats()
   7.310  		if err != nil {
   7.311  			t.Errorf("Error getting status from %T: %+v\n", store, err)
   7.312  		}